mavam
mavam

Reputation: 12552

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?

Upvotes: 18

Views: 2558

Answers (3)

heenabansal
heenabansal

Reputation: 61

Can I use other object in sidetone constructor body after using moving it like:

struct T { };
struct U { };

struct X : public T, public U
{
    X(X&& other)
      : T(std::move(other))
      , U(std::move(other)) // already moved?!
    {
    member1 = other.member1; //something like. 

    }
};

Upvotes: 1

user6547518
user6547518

Reputation:

The code in the question is fine, as the answer from @je4d explained it.

You could also write it this way :

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

See the comment of the answer from @jead for explanation.

Upvotes: 0

je4d
je4d

Reputation: 7838

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.

Upvotes: 18

Related Questions