移动构造函数
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
,即使 x
是 std::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 之前,可能存在一个临时对象,由编译器决定是否创建一个(因此,是否调用移动构造函数)。
我试图理解移动构造函数并编写了以下代码
#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
,即使 x
是 std::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 之前,可能存在一个临时对象,由编译器决定是否创建一个(因此,是否调用移动构造函数)。