This question is based on discussion below a recent blog post by Scott Meyers.
It seems "obvious" that std::swap(x, x)
should leave x
unchanged in both C++98 and C++11, but I can't find any guarantee to that effect in either standard. C++98 defines std::swap
in terms of copy construction and copy assignment, while C++11 defines it in terms of move construction and move assignment, and this seems relevant, because in C++11 (and C++14), 17.6.4.9 says that move-assignment need not be self-assignment-safe:
If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument. ... [ Note: If a program casts an lvalue to an xvalue while passing that lvalue to a library function (e.g. by calling the function with the argument move(x)), the program is effectively asking that function to treat that lvalue as a temporary. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. —end note ]
The defect report that gave rise to this wording makes the consequence clear:
this clarifies that move assignment operators need not perform the traditional if (this != &rhs) test commonly found (and needed) in copy assignment operators.
But in C++11 and C++14, std::swap
is expected to use this implementation,
template<typename T>
void swap(T& lhs, T& rhs)
{
auto temp(std::move(lhs));
lhs = std::move(rhs);
rhs = std::move(temp);
}
and the first assignment is performing an assignment to self where the argument is an rvalue. If the move assignment operator for T
follows the policy of the standard library and doesn't worry about assignment to self, this would seem to court undefined behavior, and that would mean that std::swap(x, x)
would have UB, as well.
That's worrisome even in isolation, but if we assume that std::swap(x, x)
was supposed to be safe in C++98, it also means that C++11/14's std::swap
could silently break C++98 code.
So is std::swap(x, x)
guaranteed to leave x
unchanged? In C++98? In C++11? If it is, how does this interact with 17.6.4.9's permission for move-assignment to not be self-assignment-safe?
std::swap(x, x)
, the parameterslhs
andrhs
are the same object, solhs = std::move(rhs)
is an assignment to self. – Gawlas