Search code examples
c++c++11movervalue-reference

move constructor and std::move confusion


I am reading about the std::move, move constructor and move assignment operator. To be honest, all I got now is confusion. Now I have a class:

class A{
  public:
    int key;
    int value;
    A(){key = 3; value = 4;}
    //Simple move constructor
    A(A&& B){ A.key = std::move(B.key); 
              A.value = std::move(B.value);}
};
  1. I thought B is an rvalue reference, why you can apply std::move to an ravlue reference's member?
  2. After B.key and B.value have been moved, both have been invalidated, but how B as an object of class A gets invalidated?
  3. What if I have A a(A()), A() is apparently an rvlaue, can A() be moved by std::move and why?
  4. Similarly, if I have a function

    int add(int && z){ int x = std:move(z); int y = std:move(z); return x+y; }

What if I call add(5), how can 5 be moved and why? And notice that z has been moved twice, after z has been moved first time, it has been invalidated, how can you move it again?

  1. When defining foo (T && Z )(T, Z can be anything), in the body of the definition Why on earth I should use std::move(Z) since Z is already passed by an rvalue reference and when should I use std::move?

Solution

  • std::move does not move anything, but "marks" its argument to be a rvalue reference. Technically, it converts the type to a rvalue reference. Then, the rvalue reference it's being moved by the corresponding move constructor or move assignment operator. For objects that contain only members with trivial move ctors/assignment operators, the move ctor/assignment operator is trivial and simply copies. In general, the move ctor/assignment operator of the object calls the move ctor/assignment operator of all its members.

    So, whenever you write

    int x = 10;
    int y = std::move(x); 
    

    on the right hand side of the assignment y = std::move(x), you have a rvalue reference of type int&&. However, int does not have a non-trivial move ctor, and the rvalue is simply copied into y, nothing is changed in x.

    On the other hand,

    string s = "some string";
    string moved_s = std::move(s); // here we tell the compiler that we can "steal" the resource of s
    

    is different. The move constructor of moved_s kicks in, and "steals" (i.e. swaps internal pointers etc) the resource of s, because the latter is a rvalue reference. At the end, s will not contain any element.