我有这个例子:
#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';
}
它给出输出
$ ./a.out
1. there: 1234567890
2. there: 1234567890
3. there: 1234567890
这是在linux上使用gcc 5.1.1。虽然there
字符串在移动后将保持有效但不确定的状态,但如果调用std :: string移动构造函数,则此实现似乎移动(而不是复制)字符串。
如果我用str(str_)
替换initalizer str(std::move(str_))
,我会得到此输出:
$ ./a.out
1. there: 1234567890
2. there:
3. there: 1234567890
这表明现在使用了std :: string移动构造函数,但为什么在我的第一个示例中没有调用std::string(std::string &&)
?
答案 0 :(得分:6)
你应该做
public:
Test(std::string &&str_) :
str(std::move(str_))
{}
str_
有一个名字,是一个命名对象,因此它不会作为rvalue-reference传递给任何函数。
标准委员会做出的设计选择可以防止将其视为右值,因此您无法无意中对其进行修改。特别是:str_
的类型是对string
的左值引用,但str_
不被视为右值,因为它是命名对象。
您必须通过添加对std::move
的调用来明确您的意图。这样做表明您希望str_
成为左值,并且您知道此选择的所有后果。
答案 1 :(得分:3)
因为左值参考总是赢!这就是你需要明确指定std::move
。
允许通过类型形成对引用的引用 模板或typedef中的操作,在这种情况下是引用 折叠规则适用:对右值参考折叠的右值引用 对于右值参考,所有其他组合形成左值参考:
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&&
取自here。