Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move constructors and multiple inheritance

Synopsis

How can I safely design a move constructor when a class uses multiple inheritance?

Details

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?

like image 934
mavam Avatar asked Apr 11 '12 22:04

mavam


People also ask

How are constructors called in multiple inheritance?

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.

How can you pass parameters to the constructors of base classes in multiple inheritance?

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.

Is move constructor automatically generated?

No move constructor is automatically generated.

Does STD move move constructor?

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.


1 Answers

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.

like image 116
je4d Avatar answered Oct 20 '22 04:10

je4d