How can I safely design a move constructor when a class uses multiple inheritance?
Consider the following scenario:
struct T { };
struct U { };
struct X : public T, public U
{
X(X&& other)
: T(std::move(other))
, U(std::move(other)) // already moved?!
{
}
};
Is there a way to move-construct both T
and U
safely?
For multiple inheritance order of constructor call is, the base class's constructors are called in the order of inheritance and then the derived class's constructor.
To pass arguments to a constructor in a base class, use an expanded form of the derived class' constructor declaration, which passes arguments along to one or more base class constructors. Here, base1 through baseN are the names of the base classes inherited by the derived class.
No move constructor is automatically generated.
std::move is actually just a request to move and if the type of the object has not a move constructor/assign-operator defined or generated the move operation will fall back to a copy.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With