13

I have a doubt related to Move constructors in Inheritance hierarchy. In C++ Primer (Stanley Lippman) it is mentioned that the move constructor in an inheritance hierarchy would be defined like this:

class Base { /* Default Copy control and move constructor */ };

class D : public Base {
public:
  D(const D& d) : Base(d) {/*initializers for members of D */} //This is ok
  D(D&& d): Base(std::move(d)) {/*initializers for members of D */}
};

In the move constructor, I tried removing std::move while calling the base move constructor since 'd' is a rvalue reference.

 D(D&& d): Base(d) {/*initializers for members of D */}  

But this ended up calling the base class copy constructor instead of the move constructor.

To understand why std::move is required, I searched this forum to see previous discussions and I found out a few replies which said that though 'd' is a rvalue reference, within the derived class's move constructor it is still a lvalue. Hence we need to call std::move on it to ensure that base class move constructor is called. I understood this part.

But from C++ Primer, I understand that once std::move is called we should not use the object at all after that. After the expression in which std::move is called ends, the object will remain in a valid state for destruction but the values it holds may not be meaningful.

So when we call std::move to delegate to the base class's move constructor how then would the object remain in a meaningful state when we come back to the body of the derived class's move constructor.

In other words:

D(D&& d): Base(std::move(d)) {
  // Would 'd' be in a meaningful state here?
  // After std::move(d), can I still use 'd' here?
}

I do understand that the Base class would just move the members related to the base class alone and that the derived class members won't be touched. But is this an exception where after std::move the base part of the object would be in a valid to be destructed state and the derived part would still be in a meaningful state. Please help me understand this.

MS Srikkanth
  • 3,829
  • 3
  • 19
  • 33
  • If I dont access d in the body of derived, how then will i move the derived members? – MS Srikkanth Apr 10 '14 at 01:56
  • Copy swap idiom (I didn't add a copy constructor or assignment operator): https://ideone.com/HpotCN It is just to show how moving works with hierarchical classes. Moving doesn't "destroy" the object until the move is complete (or until it goes out of scope). It just does an efficient swap or transparent copy of its internals. Since the object isn't actually "moved", you can still access its other members that weren't touched yet. Only when the move is complete is the other object considered ready for destruction. At least that's how I see it. – Brandon Apr 10 '14 at 02:25
  • @CantChooseUsernames - I don't understand why you are using std::forward. I haven't come across std::forward yet and the book that I am reading mentions std::move to be used here. Sorry about my ignorance. I am a beginner proceeding through the chapters one by one. In your code if I replace std::forward with std::move, then would swap(other, *this) be still valid? I am asking this because the book mentions that after std::move the moved from object should not be used at all. – MS Srikkanth Apr 10 '14 at 02:38
  • Yes it will still be valid. Both `std::move` and `std::forward` are just casts. It works with move as well: https://ideone.com/HCNLST One is just for perfect forwarding and gives more control. Yes you can access the parts of the object that wasn't touched by the base's move constructor.. – Brandon Apr 10 '14 at 02:45

1 Answers1

14
class Base
{
    // data members...
public:
    Base(Base&& other) = default;
};

class Derived
{
    // data members...
public:
    Derived(Derived&& other) : Base(std::move(other)) { ... }
};

The Derived move constructor casts other to an rvalue using std::move, then passes the result to the Base move constructor, which involves an implicit cast from Derived&& to Base&&.

The Base move constructor might steal the guts of the base class members of other, but it can't touch the guts of the derived class members because it only sees a Base&&.

Oktalist
  • 14,336
  • 3
  • 43
  • 63