Considering the following snippet :
class Foo
{
public:
/* ... */
Foo(Foo &&other);
~Foo();
Foo &operator=(Foo &&rhs);
private:
int *data;
};
Foo::Foo(Foo &&other)
{
data = other.data;
other.data = nullptr;
}
Foo::~Foo()
{
delete data;
}
Foo &Foo::operator=(Foo &&other)
{
if (this == &other) return *this;
delete data; /* SAME AS DESTRUCTOR */
data = other.data; /* SAME AS MOVE CONSTRUCTOR */
other.data = nullptr;
return *this;
}
This snippet is pretty much what I always end up having.
Why do we need a move operator if its behavior can be deduced ?
If this statement isn't true, in which case the move operator behave differently than just destructor + move constructor ?
Because it can't be deduced. The language doesn't know what is involved in tearing down your int*
. Maybe you have other housekeeping to perform.
In fact, if you're writing a move constructor, you'll usually have other housekeeping to perform, because if all you are doing is delete
ing dynamic memory, you should have been using a smart pointer and wouldn't need to write your own move constructor at all.
Furthermore, you're repeating logic in your code. You can "re-use the destructor" by avoiding these antics in your move constructor & assigner, simply swapping pointers instead, and let the moved-from object's destructor do what it usually does when the time comes:
Foo::Foo(Foo&& other)
: data(nullptr)
{
*this = std::move(other);
}
Foo& Foo::operator=(Foo&& other)
{
std::swap(this->data, other.data);
return *this;
};
(Disclaimer: there's probably a more idiomatic way to do this that I can't remember, but you get the point.)
Now there's much less boilerplate. So you can see that, even if the language deduced move constructors for you, it wouldn't involve a destructor at all.