为什么没有调用std::string的移动构造函数?

7

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';
}

它会输出:
$ ./a.out
1. there: 1234567890
2. there: 1234567890
3. there: 1234567890

这是在Linux上使用gcc 5.1.1。当使用std::string的移动构造函数时,虽然there字符串将保持有效但不确定的状态,但此实现似乎移动(而不是复制)字符串。

如果我将初始化器str(str_)替换为str(std::move(str_)),我会得到以下输出:

$ ./a.out
1. there: 1234567890
2. there: 
3. there: 1234567890 

这表明现在使用了std::string的移动构造函数,但为什么在我的第一个例子中没有调用std::string(std::string &&)?
2个回答

6

您应该做什么

public:
    Test(std::string &&str_) :
        str(std::move(str_))
    {}

str_有一个名称,是一个命名对象,因此不会作为rvalue-reference传递给任何函数。

标准委员会做出的设计选择防止它被视为rvalue,因此您不能无意中修改它。特别地: str_的类型是对string的lvalue引用,但str_不被视为rvalue,因为它是一个命名对象。

您必须通过添加调用std::move来明确您的意图。这样做可以说明您希望str_成为rvalue,并且您知道这种选择的所有后果。


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&&

以下内容摘自此处


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接