The question is about self-assignment. For example copying a vector into itself:
std::vector<std::string> vec(5, "hello");
vec = vec;
Should the code above perform 5 assignment operations of strings into themselves, or just do nothing? I mean whether the following checking is valid:
std::vector operator=(const std::vector &rhs)
{
if (this == &rhs)
{ return *this; }
...
}
I'm working on my own implementation of std::variant
class (just for fun) and interested if I should add a self-assignment check to the beginning of the assignment operator, or should I just copy the contained element into itself?
I understand that generally this doesn't matter. You should not make a class that utilizes the fact of copying into itself. But I'm interested if the standard says anything about this.
The pre/post conditions of assignment of a container specified by the standard (quoting latest draft):
This allows but does not mandate self assignment check.
C++ Core Guidelines recommends not to do a self-assignment check in a user class if all of its members are self-assignment safe:
It is for an efficiency reason. Self-assignments are unlikely to happen in practice. They are rare, and so it is better to avoid doing a self-assignment check in every operation. A self-assignment check probably makes the code faster in self-assignment case (very rare) and makes it slower in all other cases (more common).
Imagine you assign a million elements. In every assignment operation a self-assignment check is done. And it is most probably that it is done for nothing because none of the assignments is actually a self-assignment. And so we do a million useless checks.
If we skip doing a self-assignment check then nothing bad happens except that if self-assignment really happens then we do useless self-assignments of all members (that is sometimes slower than doing a single self-assignment check at the beginning of the assignment operator). But if your code does a million self-assignments it is a reason to reconsider your algorithm rather than to perform a self-assignment check in all of the operations.
However, self-assignment check still must be used for classes that are not self-assignment safe by default. The example is
std::vector
. The vector, that is being copied into, first has to delete the existing elements. But if the destination vector and source vector is the same object, then by deleting the elements in destination vector we also delete them in the source vector. And so it won't be possible to copy them after deletion. That is why libstdc++ does a self-assignment check forstd::vector
(though it is possible to implementstd::vector
without self-assignment check).But it doesn't do it for
std::variant
for example. If you copy a variant into itself then the contained value will be copied into itself. See live example. Because copying it into itself is self-assignment safe (provided the contained value is self-assignment safe).Thus, libstdc++ does a self-assignment check for
std::vector
(for providing self-assignment safety), and doesn't forstd::variant
(for efficiency).Checking
this == &rhs
is actually a pretty well-known idiom, and helps to be sure that you don't break anything by guaranteeing thatlhs
andrhs
are different objects. So, this is valid and actually encouraged.I don't know whether STL containers are required to do the check, though.
You should, regardless whether
std::vector
, or other STL containers do that for you. Imagine a user that works with your library and doesx = x
, outside of STL containers scope.Now to the STL container requirements - I believe the standard does not specify whether assignment is required to perform a check for being a self-assignment (went through the majority of
Containers library
section). This gives room for compiler optimisations and I believe that a decent compiler should perform such checks.