It is commonly known that when implementing an assignment operator one has to protect against self-assignment, at least when the class has non-POD members. Usually it is (or is equivalent to):
Foo& operator=(const Foo& other)
{
if (&other == this)
return *this;
... // Do copy
}
What were the reasons for not inserting the self-assignment protection automatically? Are there use cases when self-assignment does something non-trivial and practically useful?
Foo& operator=(const Foo& other)
{
if (&other == this)
{
// Do something non-trivial
}
else
{
// Do copy
}
return *this;
}
To summarize the answers and discussion by now
Looks like non-trivial self-assignment can never be really useful. The only option proposed was to put an assert
there in order to detect some logical errors. But there are quite legitimate self-assignment cases like a = std::min(a, b)
, so even this option is highly dubious.
But there are two possible implementations of a trivial self-assignment:
&other == this
. Always work, though may have negative performance impact due to an extra branching. But in a user-defined assignment operator the test must be almost always explicitly made.I still don't see why the C++ standard could not guarantee that in a user-defined assignment operator &other != this
. If you want no branching, use the default operator. If you are redefining the operator, some test is needed anyway...
Self-assignment protection is only necessary for types where the code being skipped is dangerous when applied to itself. Consider the case where you have a user-provided assignment operator because each individual object has some kind of identifier, which you don't want to copy. Well, you can "copy" the other values just fine in self-assignment cases. So inserting an invisible self-assignment test is just adding a pointless and potentially costly conditional branch.
So it's not about self-assignment being useful; it's about self-assignment not always needing protection.
Furthermore, C++ generally doesn't like adding code like that to your code without you explicitly asking for it. It's typically done in terms of whole functions, not part of functions. Even destructor calls at the end of blocks are something you asked for when you put the object to be destroyed on the stack.