I’m confused about the state of an object after it’s been moved using C++0x move semantics. My understanding is that once an object has been moved, it’s still a valid object, but its internal state has been altered so that when its destructor is called, no resources are deallocated.
But if my understanding is correct, the destructor of a moved object should still be called.
But, that doesn’t happen when I perform a simple test:
struct Foo
{
Foo()
{
s = new char[100];
cout << "Constructor called!" << endl;
}
Foo(Foo&& f)
{
s = f.s;
f.s = 0;
}
~Foo()
{
cout << "Destructor called!" << endl;
delete[] s; // okay if s is NULL
}
void dosomething() { cout << "Doing something..." << endl; }
char* s;
};
void work(Foo&& f2)
{
f2.dosomething();
}
int main()
{
Foo f1;
work(std::move(f1));
}
This output:
Constructor called!
Doing something...
Destructor called!
Notice the destructor is only called once. This shows that my understanding here is off. Why wasn’t the destructor called twice? Here’s my interpretation of what should have happened:
Foo f1is constructed.Foo f1is passed towork, which
takes an rvaluef2.- The move constructor of
Foois
called, moving all resources inf1
tof2. - Now
f2‘s destructor is called,
releasing all resources. - Now
f1‘s destructor is called,
which doesn’t actually do anything
since all resources were transferred
tof2. Still, the destructor is
called nonetheless.
But since only one destructor is called, either step 4 or step 5 isn’t happening. I did a backtrace from the destructor to see where it was being invoked from, and it’s being invoked from step 5. So why isn’t f2‘s destructor also called?
EDIT: Okay, I modified this so it’s actually managing a resource. (An internal memory buffer.) Still, I get the same behavior where the destructor is only called once.
Edit (New and correct answer)
Sorry, looking closer at the code, it seems the answer is much simpler:
you never invoke the move constructor. You never actually move the object. You just pass a rvalue reference to the
workfunction, which calls a member function on that reference, which still points back to the original object.Original answer, saved for posterity
In order to actually perform the move, you have to have something like
Foo f3(std::move(f2));insidework. Then you can call your member function onf3which is a new object, created by moving fromfAs far as I can see, you don’t get move semantics at all. You’re just seeing plain old copy elision.
for the move to happen, you have to use
std::move(or specifically, the argument being passed to the constructor has to be an unnamed/temporary) rvalue reference, such as the one that is returned fromstd::move). Otherwise it is treated as a plain old-fashioned lvalue reference, and then a copy should happen, but as usual, the compiler is allowed to optimize it away, leaving you with one object being constructed, and one object being destroyed.Anyway, even with move semantics, there’s no reason why the compiler shouldn’t do the same thing: just optimize the move, just like it’d have optimized away the copy. A move is cheap, but it’s still cheaper to just construct the object where you need it, rather than constructing one, and then moving it into another location and calling the destructor on the first one.
It’s also worth noting that you’re using a relatively old compiler, and earlier versions of the spec were very unclear on what should happen with these “zombie objects”. So it is possible that GCC 4.3 just doesn’t call the destructor. I believe it’s only the last revision, or maybe the one before it, that explicitly requires the destructor to be called