I am trying to understand how std::move and rvalues work in C++ 11. I keep seeing the similar example in tutorials like this:
Suppose we have this class :
class Class
{
public:
Class(Class &&b)
{
a = b.a;
}
int *a;
}
int main()
{
Class object1(5);
Class object2(std::move(object1));
}
After the second line in main function is ran, what happens to object1
?
If memory of object1 is "moved" to object2, what is the point of this copy constructor ? As we are losing memory of object1 just to get exact same value in a different place in memory? What is the use case of this?
Edit : Question is not a duplicate. The candidate for duplicative is much broader in the sense that it does not even have a code snippet.
Edit2 : I have just tried this piece of code :
class trial
{
public:
trial()
{
a = (int*)malloc(4);
}
trial(trial& rv)
{
this->a = rv.a;
rv.a = NULL;
}
int *a;
};
int main() {
cout << "Program Started" << endl;
trial a1;
trial a2(a1);
return 0;
}
And I have checked the internals of a1 and a2. It gives the exact same result with this code :
class trial
{
public:
trial()
{
a = (int*)malloc(4);
}
trial(trial&& rv)
{
this->a = rv.a;
rv.a = NULL;
}
int *a;
};
int main() {
cout << "Program Started" << endl;
trial a1;
trial a2(std::move(a1));
return 0;
}
The difference is with the copy constructor, one of which does not use move semantics. Just plain reference for the object. No copying occurs if we also pass by reference, but we are going to have to lose the first object somewhat by doing rv.a = NULL
, to avoid accidental memory freeing for a2
, by freeing a1
. So I set the rv.a = NULL
.
When I use rvalue copy constructor of class trial, but do not use the line rv.a = NULL
in the rvalue constructor, the integer pointer a
shows the same address both in a1
and a2
when i put a break point at the line return 0
. So how is this different than just passing by reference? It looks like we can do exactly the same by passing by reference.
Nothing.
std::move
does not move a thing. It simply casts (converts) the object to an rvalue reference, which can be seen by looking at a typical implementation :note that the
T&& arg
is a universal reference in a deducable context and not an rvalue reference per se (in case you were wondering "isn'targ
an rvalue ref already?")It's the functions that use rvalue refs, like move constructors and move assignment operators or regular functions with
&&
args, that can take advantage of this value category (it's calledxvalue
ie expiring objects) and avoid overheads by moving data out of the object, leaving it in a valid but unspecified state (eg destructible).As per EDIT 2
I think you answer your own question. Imagine you had both constructors, move and copy, in the class; what
std::move
does is let you select the first one when callingsince your implementation for both is the same, they're going to do the same thing. A typical implementation would avoid aliasing in the copy constructor case :
which means an extra allocation needs to be performed (you just want a copy, why messing with the original?).
When calling the move costructor on the other hand, you're basically telling the compiler "hey I'm not gona use
a1
anymore, do your best" and your move construction is called and you "transplant"a1
resources toa2
.