tl;dr: the code in the question is ok.
The code above is fine, because std::move
itself doesn't actually change other
in any way, it just does a cast to make other
into an rvalue reference so that the move constructors of T
and U
are called instead of their copy constructors.
When T(std::move(other))
is run, T
's move constructor will be called (assuming it has one) and the T
in other
will be moved to the T
in this
. The U
in other
will be left alone until the U(std::move(other))
is run.
Note that this means that when your move constructor code for X
runs, you cannot rely on the members/member functions of T
and U
in other
, as those bits of other
will have already have been moved.
As a side note, it could be improved by being changed to:
X(X&& other)
: T(std::move(static_cast<T&>(other)))
, U(std::move(static_cast<U&>(other)))
{
}
because this version doesn't rely on the implicit upcast from X&&
to T&&
/U&&
. Relying on the implicit upcast can be a problem because T
and/or U
may have a T(X&&)
constructor or an accept-anything template constructor, either of which would get picked instead of the T(T&&)
move constructor that you really want to call.