The standard library policy about move assignment is that the implementation is allowed to assume that self-assignment will never happen; this seems to me a really bad idea, given that:
Moved from objects in
std are supposed to be discarded or assigned to prior to being reused. Anything that is not completely free beyond that is not promised.
Sometimes things are free. Like a move-constructed-from container is empty. Note that some move-assiged-from cases have no such guarantee, as some implementations may choose to move elements instead of buffers. Why the difference? One was a free extra guarantee, the other not.
A branch or other check is not completely free. It takes up a branch prediction slot, and even if predicted is merely almost free.
On top of that,
a = std::move(a); is evidence of a logic error. Assign-from
std) means you will only assign-to or discard
a. Yet here you are wanting it to have specific state on the next line. Either you know you are self-assigning, or you do not. If you do not, you are now moving from object you are also populating and you do not know it.
The principle of "do little things to keep things safe" clashes with "you do not pay for that which you do not use". In this case, the second won.