Да, вы можете возвращать все, что хотите, от перегруженного оператора присваивания, поэтому ваш MyMovable
в порядке, но пользователи вашего кода могут быть смущены неожиданной семантикой.
Я неувидеть любую причину для возврата rvalue-ссылки в назначении перемещения. Назначение перемещения обычно выглядит так:
b = std::move(a);
и после этого b
должно содержать состояние a
, тогда как a
будет находиться в каком-либо пустом или неопределенном состоянии.
Если вы добавите цепочку таким образом:
c = b = std::move(a);
, то вы ожидаете, что b
не потеряет свое состояние, потому что вы никогда не применяли std::move
к нему. Однако если ваш оператор присваивания перемещения возвращается по rvalue-reference, то это фактически переместит состояние a
в b
, и тогда левый оператор присваивания также вызовет присваивание перемещения, передав состояние b
(который был a
раньше) до c
. Это удивительно, потому что теперь и a
, и b
имеют пустое / неопределенное состояние. Вместо этого я ожидал бы, что a
будет перемещен в b
и скопирован в c
, что именно и происходит, если назначение перемещения возвращает lvalue-ссылку.
Теперь для
c = std::move(b = std::move(a));
это работает, как вы ожидаете, вызывая назначение перемещения в обоих случаях, и было бы ясно, что состояние b
также перемещается. Но почему вы хотите это сделать? Вы могли бы перевести состояние a
в c
напрямую с помощью c = std::move(a);
без очистки состояния b
в процессе (или, что еще хуже, перевести его в состояние, которое нельзя использовать напрямую). Даже если вы этого захотите, это будет более понятно изложено в виде последовательности
c = std::move(a);
b.clear(); // or something similar
Что касается
c = std::move(b) = std::move(a)
, то, по крайней мере, ясно, что b
перемещен, но, похоже,как если бы текущее состояние b
было перемещено, а не после правого хода и снова, двойной ход является избыточным. Как вы заметили, это все еще вызывает назначение копирования для левой руки, но если вы действительно хотите сделать этот вызов назначением перемещения в обоих случаях, вы можете вернуться по rvalue-reference и избежать проблемы с c = b = std::move(a)
, описанной выше, вам нужно будет дифференцировать категорию значений среднего выражения. Это можно сделать, например, следующим образом:
MyMovable& operator=(MyMovable&& other) & {
...
return *this;
}
MyMovable&& operator=(MyMovable&& other) && {
return std::move(operator=(std::move(other)));
}
Спецификаторы &
и &&
означают, что следует использовать конкретную перегрузку, если выражение, для которого вызывается функция-член, является lvalue или rvalue.
Я не знаю, есть ли какой-нибудь случай, который вы бы на самом деле хотели сделать это.