为什么不调用 std::string move 构造函数?

Why wasn't std::string move constructor called?

本文关键字:构造函数 move string std 为什么不 调用      更新时间:2023-10-16

我有这个例子:

#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(std::move(str_))替换初始化器str(str_),我会得到以下输出:

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

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

您应该执行

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

str_确实有一个名称,是一个命名对象,所以它不会作为右值引用传递给任何函数。

标准委员会做出的设计选择可以防止将其视为右值,因此您不会无意中对其进行修改。特别是:str_-do的类型是对string的左值引用,但str_不被认为是右值,因为它是一个命名对象。

您必须通过向std::move添加一个调用来明确您的意图。这样做意味着你希望str_是一个右值,并且你知道这个选择的所有后果。

因为左值引用总是获胜!这就是为什么需要显式指定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&&

从这里拍摄。

相关文章: