What happens to an object instance after applying std::move
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 :
template <typename T>
typename remove_reference<T>::type&& move(T&& arg)
{
return static_cast<typename remove_reference<T>::type&&>(arg);
}
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't arg
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 called xvalue
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 calling
trial a2(std::move(a1));
since 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 :
trial(trial& rv)
{
this->a = (int*)malloc(sizeof(int));
this->a = rv.a;
}
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 to a2
.
What happens to an object instance after applying std::move?"
Nothing. It'll be treated as any other object after that. This means that the destructor will still be called. As rems4e already mentioned, you should transfer the state (e.g. by copying pointers, because that's cheap) and leave the original object with no references to it's former resources (if the destructor tries to free them as it should) or some other defined state.
"After the second line in main function is ran, what happens to
object1
?"
You hit a scope exit }
and this triggers a destructor call. First on object2
, then on object1
.
If memory of
object1
is "moved" toobject2
, what is the point of this copy constructor? As we are losing memory ofobject1
just to get exact same value in a different place in memory? What is the use case of this?
Think of it as a specialization. While the real copy constructor enables you to duplicate an object (deep down to its leafs, e.g. when doing an assignment of object1
to object2
) which could be very, very expensive, the move constructor enables you to transfer a state quickly by just copying the pointers of its members. This comes in handy when returning from a function.
Here's an example:
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class Person {
private:
shared_ptr<string> name;
public:
Person(shared_ptr<string> name) {
cout << "Constructing " << *name << endl;
this->name = name;
}
Person(const Person& original) {
cout << "Copying " << *original.name << endl;
name = make_shared<string>("Copy of " + *original.name);
}
Person(Person&& original) {
cout << "Moving " << *original.name << endl;
name = make_shared<string>(*original.name + ", the moved one");
original.name = make_shared<string>("nobody (was " + *original.name + ")");
}
~Person() {
cout << "Destroying " << *name << endl;
name = make_shared<string>();
}
};
Person give_it_here(shared_ptr<string> name) {
return Person(name);
}
int main(int argc, char* argv[]) {
{
Person p1(make_shared<string>("John"));
Person p2 = move(p1); // Unnecessarily moving to another variable. It makes no sense.
}
cout << endl;
{
Person p1(make_shared<string>("James"));
Person p2 = p1; // Copying here. Could make sense, but it depends.
}
cout << endl;
{
Person p1 = give_it_here(make_shared<string>("Jack")); // Let some other function create the object and return (move) it to us.
}
return 0;
}
The code prints (using g++ with C++11 and -fno-elide-constructors
)
Constructing John Moving John Destroying John, the moved one Destroying nobody (was John) Constructing James Copying James Destroying Copy of James Destroying James Constructing Jack Moving Jack Destroying nobody (was Jack) Moving Jack, the moved one Destroying nobody (was Jack, the moved one) Destroying Jack, the moved one, the moved one
Remarks:
- That flag
-fno-elide-constructors
is required to prevent return value optimzation (for this example) - For some reason that eludes me g++ generates two moves instead of one in the last example