Something bothers me about:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
swap(tmp);
return *this;
}
First, reading the word "swap" when my mind is thinking "copy" irritates my common sense. Also, I question the goal of this fancy trick. Yes, any exceptions in constructing the new (copied) resources should happen before the swap, which seems like a safe way to make sure all the new data is filled before making it go live.
That's fine. So, what about exceptions that happen after the swap? (when the old resources are destructed when the temporary object goes out of scope) From the perspective of the user of the assignment, the operation has failed, except it didn't. It has a huge side effect: the copy did actually happen. It was only some resource cleanup that failed. The state of the destination object has been altered even though the operation seems from the outside to have failed.
So, I propose instead of "swap" to do a more natural "transfer":
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
transfer(tmp);
return *this;
}
There's still the construction of the temporary object, but the next immediate action is to free all current resources of the destination before moving (and NULLing so they won't be double-freed) the resources of the source to it.
Instead of { construct, move, destruct }, I propose { construct, destruct, move }. The move, which is the most dangerous action, is the one taken last after everything else has been settled.
Yes, destruction fail is a problem in either scheme. The data is either corrupted (copied when you didn't think it was) or lost (freed when you didn't think it was). Lost is better than corrupted. No data is better than bad data.
Transfer instead of swap. That's my suggestion anyway.