I have this example:
#include <string>
#include <iostream>
class Test {
private:
std::string str;
public:
Test(std::string &&str_) :
str(str_)
{}
const std::string &GetStr()
{
return str;
}
};
int main(int argc, char *argv[])
{
std::string there("1234567890");
std::cout << "1. there: " << there << '\n';
Test t1(std::move(there));
std::cout << "2. there: " << there << '\n';
std::cout << "3. there: " << t1.GetStr() << '\n';
}
It gives the output
$ ./a.out
1. there: 1234567890
2. there: 1234567890
3. there: 1234567890
This is using gcc 5.1.1 on linux. While the there
string will be left in valid but indeterminate state after the move, this implementation seems to move (and not copy) the string if the std::string move constructor is invoked.
if I replace the initalizer str(str_)
with str(std::move(str_))
I get this output:
$ ./a.out
1. there: 1234567890
2. there:
3. there: 1234567890
This suggest the std::string move constructor is now used, but why isn't std::string(std::string &&)
invoked in my first example ?
You should do
public:
Test(std::string &&str_) :
str(std::move(str_))
{}
str_
do have a name, is a named object, so it won't be passed to any function as an rvalue-reference.
A design choice made by the Standard committee prevents it to be treated as an rvalue, so you can't inadvertently have it modified. In particular: the type of str_
do is an lvalue reference to string
, but str_
is not considered an rvalue, because it's a named object.
You must make your intention explicit by adding a call to std::move
. Doing so you state that you want str_
to be an rvalue and you know all the consequences of this choice.
Because lvalue-reference always wins! That's why you need to explicitly specify std::move
.
It is permitted to form references to references through type manipulations in templates or typedefs, in which case the reference collapsing rules apply: rvalue reference to rvalue reference collapses to rvalue reference, all other combinations form lvalue reference:
typedef int& lref;
typedef int&& rref;
int n;
lref& r1 = n; // type of r1 is int&
lref&& r2 = n; // type of r2 is int&
rref& r3 = n; // type of r3 is int&
rref&& r4 = 1; // type of r4 is int&&
Taken from here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With