Consider:
#include <cstdlib>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
class Gizmo
{
public:
Gizmo() : foo_(shared_ptr<string>(new string("bar"))) {};
Gizmo(Gizmo&& rhs); // Implemented Below
private:
shared_ptr<string> foo_;
};
/*
// doesn't use std::move
Gizmo::Gizmo(Gizmo&& rhs)
: foo_(rhs.foo_)
{
}
*/
// Does use std::move
Gizmo::Gizmo(Gizmo&& rhs)
: foo_(std::move(rhs.foo_))
{
}
int main()
{
typedef vector<Gizmo> Gizmos;
Gizmos gizmos;
generate_n(back_inserter(gizmos), 10000, []() -> Gizmo
{
Gizmo ret;
return ret;
});
random_shuffle(gizmos.begin(), gizmos.end());
}
In the above code, there are two versions of Gizmo::Gizmo(Gizmo&&)
-- one uses std::move
to actually move the shared_ptr
, and the other just copies the shared_ptr
.
Both version seem to work on the surface. One difference (the only difference I can see) is in the non-move
version the reference count of the shared_ptr
is temporarily increased, but only briefly.
I would normally go ahead and move
the shared_ptr
, but only to be clear and consistent in my code. Am I missing a consideration here? Should I prefer one version over the other for any technical reason?
The main issue here is not the small performance difference due to the extra atomic increment and decrement in shared_ptr
but that the semantics of the operation are inconsistent unless you perform a move.
While the assumption is that the reference count of the shared_ptr
will only be temporary there is no such guarantee in the language. The source object from which you are moving can be a temporary, but it could also have a much longer lifetime. It could be a named variable that has been casted to an rvalue-reference (say std::move(var)
), in which case by not moving from the shared_ptr
you are still maintaining shared ownership with the source of the move, and if the destination shared_ptr
has a smaller scope then the lifetime of the pointed object will needlessly be extended.