Ответ 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&&)
, который вы действительно хотите позвонить.