Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move constructors and inheritance

Tags:

I am trying to understand the way move constructors and assignment ops work in C++11 but I'm having problems with delegating to parent classes.

The code:

class T0 { public:     T0() { puts("ctor 0"); }     ~T0() { puts("dtor 0"); }     T0(T0 const&) { puts("copy 0"); }     T0(T0&&) { puts("move 0"); }     T0& operator=(T0 const&) { puts("assign 0"); return *this; }     T0& operator=(T0&&) { puts("move assign 0"); return *this; } };  class T : public T0 { public:     T(): T0() { puts("ctor"); }     ~T() { puts("dtor"); }     T(T const& o): T0(o) { puts("copy"); }     T(T&& o): T0(o) { puts("move"); }     T& operator=(T const& o) { puts("assign"); return static_cast<T&>(T0::operator=(o)); }     T& operator=(T&& o) { puts("move assign"); return static_cast<T&>(T0::operator=(o)); } };  int main() {     T t = std::move(T());     return 0; } 

However, when I compile and run under VS2012, the output indicates that the lvalue versions of the T0 members are called:

ctor 0 ctor copy 0  <-- move    <-- dtor dtor 0 dtor dtor 0 

A similar situation (with a slightly different test case) happens with move assignments -- the move assignment operator of T calls the "normal" assignment operator of T0.

What am I doing wrong?

like image 906
Alex O Avatar asked Mar 12 '13 00:03

Alex O


People also ask

What are move constructors?

A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying. For more information about move semantics, see Rvalue Reference Declarator: &&. This topic builds upon the following C++ class, MemoryBlock , which manages a memory buffer.

Is move constructor automatically generated?

No move constructor is automatically generated.

Does STD move move constructor?

std::move is actually just a request to move and if the type of the object has not a move constructor/assign-operator defined or generated the move operation will fall back to a copy.

What is the difference between a move constructor and a copy constructor?

If any constructor is being called, it means a new object is being created in memory. So, the only difference between a copy constructor and a move constructor is whether the source object that is passed to the constructor will have its member fields copied or moved into the new object.


2 Answers

One of the more confusing things about functions taking rvalue references as parameters is that internally they treat their parameters as lvalues. This is to prevent you from moving the parameter before you mean to, but it takes some getting used to. In order to actually move the parameter, you have to call std::move (or std::forward) on it. So you need to define your move constructor as:

T(T&& o): T0(std::move(o)) { puts("move"); } 

and your move assignment operator as:

T& operator=(T&& o) { puts("move assign"); return static_cast<T&>(T0::operator=(std::move(o))); } 
like image 82
Joe Gottman Avatar answered Oct 19 '22 09:10

Joe Gottman


You're only ever calling your base class's stuff with lvalues:

void foo(int&){}  // A void foo(int&&){} // B  void example(int&& x) {     // while the caller had to use an rvalue expression to pass a value for x,     // since x now has a name in here it's an lvalue:     foo(x); // calls variant A }  example(std::move(myinteger)); // rvalue for us, lvalue for example 

That is, you need:

T(T&& o): T0(std::move(o)) // rvalue derived converts to rvalue base {     puts("move"); } 

And:

T& operator=(T&& o) {     puts("move assign");      T0::operator=(std::move(o)));      return *this; } 
like image 39
GManNickG Avatar answered Oct 19 '22 10:10

GManNickG