Reputation: 37
#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?
Upvotes: 1
Views: 73
Reputation: 122516
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&&)
Upvotes: 1
Reputation: 172934
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;
}
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;
}
Upvotes: 4