54

When you have a derived object with a move constructor, and the base object also has move semantics, what is the proper way to call the base object move constructor from the derived object move constructor?

I tried the most obvious thing first:

 Derived(Derived&& rval) : Base(rval)
 { }

However, this seems to end up calling the Base object's copy constructor. Then I tried explicitly using std::move here, like this:

 Derived(Derived&& rval) : Base(std::move(rval))
 { }

This worked, but I'm confused why it's necessary. I thought std::move merely returns an rvalue reference. But since in this example rval is already an rvalue reference, the call to std::move should be superfluous. But if I don't use std::move here, it just calls the copy constructor. So why is the call to std::move necessary?

Alex Bitek
  • 6,273
  • 4
  • 44
  • 77
Channel72
  • 22,459
  • 30
  • 97
  • 168

3 Answers3

47

rval is not a Rvalue. It is an Lvalue inside the body of the move constructor. That's why we have to explicitly invoke std::move.

Refer this. The important note is

Note above that the argument x is treated as an lvalue internal to the move functions, even though it is declared as an rvalue reference parameter. That's why it is necessary to say move(x) instead of just x when passing down to the base class. This is a key safety feature of move semantics designed to prevent accidently moving twice from some named variable. All moves occur only from rvalues, or with an explicit cast to rvalue such as using std::move. If you have a name for the variable, it is an lvalue.

Ajay
  • 16,823
  • 9
  • 50
  • 94
Chubsdad
  • 23,089
  • 4
  • 57
  • 108
  • Just to add a note regardless of move constructor if you provide an argument input as regular lvalue (not using the std::move(xx)) you will get "cannot bind ‘Xyy’ lvalue to ‘Xyy&&’ for a function defined as foo(Xyy&& xyy). – Kemin Zhou Mar 23 '17 at 19:51
0

Named R-value references are treated as L-value.

So we need std::move to convert it to R-Value.

foobar
  • 2,596
  • 1
  • 22
  • 48
-5

You really should use std::forward(obj) rather than std::move(obj). Forward will return the proper rvalue or lvalue based on the what obj is whereas move will turn an lvalue into an rvalue.

jbreiding
  • 352
  • 2
  • 6
  • 19
    In this context you always want to cast to an rvalue. – Howard Hinnant Feb 01 '11 at 20:02
  • 2
    So the real intent is forward exactly what was passed in not to change what was passed in. Forward() does exactly that. It would also get you into the practice of using that for calling all base functions no matter what is passed in. Getting in the habit of calling move() might yield mistakes that could be difficult to trace. Also this comes in handy if you wanted to use templates on a function that can act on both an rvalue and a an lvalue. If this fucntion was mistakenly passed in an object that is an lvalue would want it to continue execution? Just playing devils advocate a little bit – jbreiding Feb 02 '11 at 17:31
  • 1
    @jbreiding I dont understand any of this... mind elaborating on your explanation with some code samples on when std::move will fail, why std::forward is preferred and when to use which? – aCuria Mar 10 '12 at 16:25
  • 1
    @jbreiding See Scott Meyers' *Effective Modern C++*, items 23 and 25. To quote part of it: "`std::forward` requires both a function argument and a template type argument...the type we pass to `std::forward` should be a non-reference...this means that `std::move` requires less typing than `std::forward`, and it spares us the trouble of passing a type argument that encodes that the argument we're passing is an rvalue. It also eliminates the possibility of our passing an incorrect type...which [could] result in the data member [] being copy constructed instead of move constructed." – Kyle Strand May 26 '15 at 17:00
  • @aCuria `std::move` will be the wrong choice whenever you're not "done with" the object in question. Of course this is never the case inside of move constructors, but when attempting perfect-forwarding, using `move` would cause the wrong function to be called if there is an overload on rvalue-ref. – Kyle Strand May 26 '15 at 17:05
  • 1
    `std::forward` is a dirty overload helper for lvalues and rvalues. It should be used, when you want a function template to forward both: lvalues and rvalues. Here all you want to do is to call parent class move-constructor, so `std::move` is more explicit and straightforward solution. – doc Mar 15 '17 at 16:51