Behaviour of move-assignment to self
c++, move-semantics
Solution
§17.6.4.9 [res.on.arguments]:
Each of the following applies to all arguments to functions defined in the C++ standard library, unless explicitly stated otherwise.
- [...]
- 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 the parameter is a generic parameter of the form `T&&` and an lvalue of type `A` is bound, the argument binds to an lvalue reference (14.8.2.1) and thus is not covered by the previous sentence. — end note ] [ 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 ]
Since "the implementation may assume that this parameter is a unique reference to this argument", and self-move-assignment would violate this assumption, it has undefined behavior.
See also LWG issue 1204.
Problem
Example code: ``` #include <iostream> int main() { std::vector<int> w(20, 123), x; w = std::move(w); std::cout << w.size() << std::endl; } ``` Output on g++ 4.8.3: `0` Of course, the standard says that the move-assignment operator leaves the operand in an unspecified state. For example if the code were `x = std::move(w);` then we would expect `w.size()` to be zero. However, is there a specified ordering or other clause that covers the self-move case? Is it unspecified whether size is `0` or `20`, or something else, or undefined behaviour? Do the standard containers have any defined semantics here? Related: this thread talks about whether you should care about self-move in your own classes, but does not discuss whether standard containers' move-assignment operators do, and doesn't provide Standard references. NB. Is this exactly identical to `w = static_cast< std::vector<int> && >(w);` or does the fact that `std::move` is a function make a difference?