Известно, что при реализации копирования в C++ принято учитывать случай, когда мы хотим скопировать объект в себя:
Foo &operator=(const Foo &other) {
if (this == &other) {
return *this;
}
// ...
return *this;
}
Вопрос: надо ли учитывать аналогичный случай при реализации перемещающего присваивания? Давайте посмотрим, что делает STL на примере вектора:std::vector<int> v{1, 2, 3, 4};
v = std::move(v);
std::cout << v.size() << std::endl;
Думаете, что этот код выведет 4? А вот и нет! При компиляции в GCC под libstdc++ мы получим вывод 0 (т.е. вектор опустеет)! То есть, авторы STL не считают, что перемещение объекта самого в себя должно быть эквивалентно no-op и при реализации вектора не костылили этот случай.Почему так? Пойдем на StackOverflow и возьмем оттуда цитату из Стандарта:
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]
Здесь приводится более общее утверждение: считается, что если функция из STL принимает rvalue-ссылку на объект, то ссылка считается уникальной. А в случае перемещения в самого себя нам надо еще иметь вторую ссылку на тот же объект: то, куда мы присваиваем.
Стоит отметить, что правила выше относятся только к STL, но не к пользовательскому коду. Но если перемещение в себя не работает даже со стандартными классами, то есть ли вообще какой-то смысл поддерживать этот случай в своем коде?