This question already has answers here:
Closed 5 years ago.
I'm trying to call the base class move ctor explicitly through derived class move ctor but, surprise!, that actually calls the base class copy ctor NOT the base class move ctor.
I'm using std::move()
function on an object to be sure that the derived move ctor is being invoked!
The code:
class Base
{
public:
Base(const Base& rhs){ cout << "base copy ctor" << endl; }
Base(Base&& rhs){ cout << "base move ctor" << endl; }
};
class Derived : public Base
{
public:
Derived(Derived&& rhs) : Base(rhs) { cout << "derived move ctor"; }
Derived(const Derived& rhs) : Base(rhs) { cout << "derived copy ctor" << endl; }
};
int main()
{
Derived a;
Derived y = std::move(a); // invoke move ctor
cin.ignore();
return 0;
}
PROGRAM OUTPUT:
base copy ctor
derived move ctor
As you see, the base class move ctor is being forgotten, so how do I call it?
In the context of your Derived
class the parameter rhs
clearly has a name. Thus, it must be an lvalue, it can't be an rvalue. However, the T&&
only binds to rvalues. If you want to call the base class's move constructor you need to use code like this:
Derived(Derived&& rhs): Base(std::move(rhs)) { std::cout << "derived move ctor"; }
This will call the move constructor of Base
and move the Base
portion of rhs
. Since Base
doesn't know anything about the Derived
members, the Base
move constructor won't move anything added by Derived
.
A constructor, or any other function or method, with &&
in its signature will only be eligible to be selected by the compiler if both these conditions hold:
- The datatype of the expression you are passing in is
T&&
or T
. - i.e. T&
won't be accepted
- and it actually has to be an rvalue - e.g. returned (by value
T
or by T&&
) from a function.
move(rhs)
satisfies both of these conditions. rhs
is of the right type, but it actually has to be returned from a function (such as move
) before it can be considered eligible to be passed into a function that requires an &&
.
If the base class move constructor were used, then the derived constructor could access a moved-from object. That's dangerous, so it won't happen unless you explicitly tell the compiler that you're done using the object and it's safe to move.