18

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?

3 Answers 3

18

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.

Sign up to request clarification or add additional context in comments.

8 Comments

until someone in calls a virtual function or something in a move constructor/assignment operator which is addressed by another class in inheritance tree... rihgt?
Why not T(static_cast<T&&>(other)) rather than T(std::move(static_cast<T&>(other))) (and the same for U)?
@ildjarn that would also work if T is a concrete class, but could be a problem if T was a template parameter because of reference collapsing. I was trying to avoid "inlining" std::move to make it clearer what I was changing
There are no templates in this example, and if there were you would probably want std::forward rather than std::move. I think using move here confuses things, personally, but I see what you're getting at. :-]
@ildjarn i'm not sure what you're getting at with std::forward, but then i'm not even sure whether you're thinking of a template constructor or a template class. My point about reference collapsing was a dud, because even if T and U were template args, they'd have to be real classes as they're inherited from. Still, since we are doing a move, I think it's clearer to do the explicit upcast and the move separately. It also encourages the use of std::move for doing lvalue-to-rvalue conversions over writing casts manually, which I think is a good thing.
|
1

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. 

    }
};

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.