移动构造函数

Move constructor

我试图理解移动构造函数并编写了以下代码

#include<iostream>
#include<string>
using namespace std;

class mystring
{
    string s;
public:
    mystring(const string& x):
    s(x)
    {

    }
    mystring(const mystring& x)

    {
        cout<<"Copy called"<<endl;
        s = x.s;
    }


    mystring(const mystring&& x)
    {
        cout<<"Move Called"<<endl;
        s = x.s;
    }

    mystring& operator+(const mystring& x)
    {
        cout<<"+ operator"<<endl;
        s = s+x.s;
        return *this;
    }
};

int main()
{
    string a = "Hello ";
    string b = "World ";
    mystring a1(a);
    mystring b1(b);
    mystring c = mystring(a1+b1);
}

我期望在 a1+b1 的结果 rValue 上调用移动构造函数,但我看到只调用了复制构造函数。我错过了什么吗?

gcc --version
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0

在 HolyBlackCat 的回答后编辑:

#include<iostream>
#include<string>
using namespace std;

class mystring
{
    string s;
public:
    explicit mystring(const string& x):
    s(x)
    {
        cout<<"ctor called"<<endl;
    }
    mystring(const mystring& x)

    {
        cout<<"Copy called"<<endl;
        s = x.s;
    }


    mystring(mystring&& x)
    {
        cout<<"Move Called"<<endl;
        s = std::move(x.s);
    }

    mystring operator+(const mystring& x) const
    {
        cout<<"+ operator"<<endl;
        return mystring(s+x.s);
    }
};

int main()
{
    string a = "Hello ";
    string b = "World ";
    mystring a1(a);
    mystring b1(b);
    mystring c(a1+b1) ;
} 

移动构造函数仍未被调用:

ctor called
ctor called
+ operator
ctor called

要移动某些东西,需要对其进行修改。您正在接受 const mystring&&const mystring& 已经可以绑定到左值和右值,因为它是常量并且不能被修改。

您的移动构造函数应该采用 none const 右值引用,mystring&& x

这里的第二个问题是,即使这里的 x 是右值引用类型,x 仍然被命名并且被认为是移动构造函数中的左值。

要实际移动值,我们需要使用 std::move.

进行转换
mystring(mystring&& x)
{
    cout<<"Move Called"<<endl;
    s = std::move(x.s);
}

没有理由调用移动构造函数,因为您的 operator+ 不是 return 临时的。


修改operator+中的左操作数是个坏主意。没有人期望 a + b 修改 a.

您应该将其重写为mystring operator+(const mystring& x) const。这样它会 return 一个临时的,你的移动构造函数应该被调用(除非编译器 optimizes it away)。

此外,通常移动constructor/assignment的参数应该是const右值引用。您需要能够修改参数以从中移动资源。

另外,请注意右值引用是左值。这听起来可能很奇怪,但重点是如果没有 std::move,即使 xstd::string &&s = x.s 也会复制字符串。

最后,移动构造函数应该是这样的:

mystring(mystring &&x)
{
    cout << "Move Called" << endl;
    s = std::move(x.s);
}

或者更好,像这样:

mystring(mystring &&x) : s(std::move(x.s))
{
    cout << "Move Called" << endl;
}

您的代码未调用任何移动操作,因此未调用移动构造函数。

在第二个代码中:从C++17开始,mystring c(a1+b1);表示a1+b1的结果对象是c。这并不意味着有一个临时对象移动到 c.

在 C++17 之前,可能存在一个临时对象,由编译器决定是否创建一个(因此,是否调用移动构造函数)。