1
#include <iostream>

using namespace std;

class Test {
public:
    
    Test(string value){
        cout<<"Ctor "<<value<<endl;
        _val=value;
    }
    Test( Test&& mv): _val(mv._val)
    {
        mv._val=string();
        cout<<"Mv constructor"<<endl;  
    }
    
    string& get()
    {
      return this->_val;
    }
private:
    string _val;
};

void print(Test&& t)
{
    cout<<"Stampa val is "<<t.get()<<endl;
}

int main()
{
    Test a{"ciao"};
    print(move(a));
    cout<<"Val of a is "<<a.get()<<endl;
    print(Test("test"));
    

    return 0;
}

The output of this is (adding line numbers to stdout):

Ctor ciao
Stampa val is ciao
Val of a is ciao
Ctor test
Stampa val is test

Why at line 2 in main the mv semantic is not called? I might understand at line four there is an optimisation so the constructor only is called but, I can't explain the first move. Any ideas?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
HDenied
  • 37
  • 6
  • 1
    `std::move()` only casts to an rvalue, it doesn't do anything really. – flowit May 20 '21 at 09:53
  • Because there is no need to copy/move Test instance when calling `print`. `print` takes Rvalue reference, and you pass Rvalue reference casting `a` by `move`. BTW, fix your move ctor: `_val(std::move(mv._val))`. – rafix07 May 20 '21 at 09:54
  • 1
    Move constructor is a constructor, which means it constructs a new object. In `print(move(a));` statement, there is simply no new object involved, so there is consequently no constructor called. – Daniel Langr May 20 '21 at 10:06

2 Answers2

4

std::move just converts argument to rvalue (which could be moved later), it doesn't perform move operation itself. The converted rvalue is bound to the reference parameter t, so the move constructor isn't invoked in this case.

std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object.

In particular, std::move produces an xvalue expression that identifies its argument t. It is exactly equivalent to a static_cast to an rvalue reference type.

Move constructor is typically called to initialize an object, it won't be called in reference binding (it's true for lvalue-reference too). If you change the parameter to be passed by-value, move constructor would be used (to initialize the parameter). E.g.

void print(Test t)
{
    cout<<"Stampa val is "<<t.get()<<endl;
}

LIVE


BTW: Even after change to pass-by-value print(Test("test")); doesn't invoke move constructor because of copy elision.

BTW2: In the move constructor it's better to move initialize data member val based on move operation provided by std::string. E.g.

Test( Test&& mv): _val(std::move(mv._val))
{
    cout<<"Mv constructor"<<endl;  
}
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Ok, thanks! But after the conversion why shouldn't it be called? Is it an optimisation, is it not expected to call the move semantic? Or is this the case where we use the double && to say we jsut expect an rvalue therefore the mv constructor is not called? – HDenied May 20 '21 at 09:59
  • @HDenied Answer revised. – songyuanyao May 20 '21 at 10:04
  • 2
    @HDenied `T&&` is a reference to a `T`. It only differs from `T&` in what can be bound to the reference – Caleth May 20 '21 at 10:05
  • @songyuanyao thanks, the part related to initialisation and reference binding in particular made me understand the concept – HDenied May 20 '21 at 10:35
1

There are only two Test objects constructed in your code, not more.

int main()
{
    Test a{"ciao"};                          // here 
    print(move(a));
    cout<<"Val of a is "<<a.get()<<endl;
    print(Test("test"));                     // and here
}

The second also constructs the object via Test(string) constructor. std::move does not construct an object, it is merely a cast to a rvalue reference. Passing that rvalue reference to print also does not require to construct another Test. If you want to call the constructor you have to actually construct an instance, for example:

 auto t  = Test( std::move(a) );     // calls Test(Test&&)
 auto t2 = Test( Test("move me") );  // calls Test(string) then Test(Test&&)
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185