Make move assignment safe for self-assignment
Reason
If x = x changes the value of x, people will be surprised and bad errors can occur. However, people don't usually directly write a self-assignment that turns into a move, but it can occur. However, std::swap is implemented using move operations so if you accidentally do swap(a, b) where a and b refer to the same object, failing to handle self-move could be a serious and subtle error.
Example
class Foo {
string s;
int i;
public:
Foo& operator=(Foo&& a) noexcept;
// ...
};
Foo& Foo::operator=(Foo&& a) noexcept // OK, but there is a cost
{
if (this == &a) return *this; // this line is redundant
s = std::move(a.s);
i = a.i;
return *this;
}
The one-in-a-million argument against if (this == &a) return *this; tests from the discussion of self-assignment is even more relevant for self-move.
Note
There is no known general way of avoiding an if (this == &a) return *this; test for a move assignment and still getting a correct answer (i.e., after x = x the value of x is unchanged).
Note
The ISO standard guarantees only a "valid but unspecified" state for the standard-library containers. Apparently this has not been a problem in about 10 years of experimental and production use. Please contact the editors if you find a counter example. The rule here is more caution and insists on complete safety.
Example
Here is a way to move a pointer without a test (imagine it as code in the implementation a move assignment):
// move from other.ptr to this->ptr
T* temp = other.ptr;
other.ptr = nullptr;
delete ptr; // in self-move, this->ptr is also null; delete is a no-op
ptr = temp; // in self-move, the original ptr is restored
Enforcement
- (Moderate) In the case of self-assignment, a move assignment operator should not leave the object holding pointer members that have been
deleted or set tonullptr. - (Not enforceable) Look at the use of standard-library container types (incl.
string) and consider them safe for ordinary (not life-critical) uses.