Переместить конструкторы и множественное наследование

Описание

Как я могу безопасно конструировать конструктор перемещения, когда класс использует множественное наследование?

Подробнее

Рассмотрим следующий сценарий:

struct T { };
struct U { };

struct X : public T, public U
{
    X(X&& other)
      : T(std::move(other))
      , U(std::move(other)) // already moved?!
    {
    }
};

Есть ли способ переместить-построить как T, так и U безопасно?

Ответ 1

tl; dr: код в вопросе в порядке.

Код выше отлично, потому что сам std::move фактически не меняет other, он просто делает бросок, чтобы сделать other ссылкой на rvalue, чтобы конструкторы перемещения T и U вызывается вместо их конструкторов копирования.

Когда выполняется T(std::move(other)), будет вызываться конструктор move T (предполагается, что он имеет один), а T в other будет перемещен в T в this. U в other будет оставлен в покое, пока не будет запущен U(std::move(other)).

Обратите внимание, что это означает, что при выполнении кода конструктора перемещения для X вы не можете полагаться на функции members/member от T и U в other, так как эти биты other будут иметь уже перемещены.


В качестве побочного примечания его можно было бы улучшить, изменив его на:

X(X&& other)
  : T(std::move(static_cast<T&>(other)))
  , U(std::move(static_cast<U&>(other)))
{
}

потому что эта версия не полагается на неявный upcast от X&& до T&&/U&&. Опираясь на неявное повышение может быть проблемой, потому что T и/или U может иметь конструктор T(X&&) или конструктор шаблона accept-anything, любой из которых будет выбран вместо конструктора перемещения T(T&&), который вы действительно хотите позвонить.