C++ 为什么要使用移动构造函数?
对于为什么要使用/需要移动构造函数,我有点困惑。C++ 为什么要使用移动构造函数?,c++,move-semantics,move-constructor,C++,Move Semantics,Move Constructor,对于为什么要使用/需要移动构造函数,我有点困惑。 如果我有以下资料: vector fill(istream& is) { vector res; for(double x; is >> x; res.push_back(x)); return res; } void bar() { vector vec = fill(cin); // ... use vec ... } 我可以通过添加vectorfill(istream&is,v
如果我有以下资料:
vector fill(istream& is)
{
vector res;
for(double x; is >> x; res.push_back(x));
return res;
}
void bar()
{
vector vec = fill(cin);
// ... use vec ...
}
我可以通过添加vectorfill(istream&is,vector&res)
来消除返回res的需要,从而不调用复制构造函数
那么,有一个移动构造函数有什么意义呢?假设您接下来将
std::vector
放入一个std::vector
(如果您认为向量不应该嵌套,那么假设内部类型为std::string
,并假设我们正在讨论std::string
的移动构造函数):即使您可以添加一个空对象并将其填充到位,但最终在调整大小时需要重新定位向量,此时移动元素很方便
请注意,从函数返回并不是move构造的主要动机,至少在效率方面不是这样:在效率很重要的情况下,构建代码以启用复制省略甚至通过避免移动来进一步提高性能
不过,move构造函数在语义上可能仍然相关,因为返回时要求类型是可复制的或可移动的。某些类型,例如流,是不可复制的,但它们是可移动的,可以通过这种方式返回。假设您接下来将
std::vector
放入std::vector
(如果您认为向量不应该嵌套,那么假设内部类型为std::string
,并假设我们正在讨论std::string
的移动构造函数):即使您可以添加一个空对象并将其填充到位,最终在调整大小时需要重新定位向量,此时移动元素很方便
请注意,从函数返回并不是move构造的主要动机,至少在效率方面不是这样:在效率很重要的情况下,构建代码以启用复制省略甚至通过避免移动来进一步提高性能
但是,移动构造函数在语义上可能仍然相关,因为返回要求类型是可复制的或可移动的。某些类型(例如流)不可复制,但它们是可移动的,可以通过这种方式返回。在您的示例中,编译器可能应用RVO-返回值优化,这意味着您的函数将是内联的,所以不会发生返回,也不会应用移动语义。只有当它不能应用RVO时,才会使用移动构造函数(如果可用)
在引入移动语义之前,人们使用各种技术来模拟它们。其中之一实际上是通过引用返回值。在您的示例中,编译器可能会应用RVO-返回值优化,这意味着您的函数将内联,因此不会发生返回,也不会应用移动语义。只有当无法应用RVO-将使用移动构造函数(如果可用)
在引入移动语义之前,人们使用各种技术来模拟它们。其中一种技术实际上是通过引用返回值。一个原因是,使用赋值运算符可以更容易地掌握每一行在做什么。如果有一个函数调用
somefunction(var1、var2、var3)
,不清楚其中一些函数是否被修改。要找到答案,您必须实际阅读另一个函数
此外,如果必须将向量作为参数传递给fill()
,这意味着调用函数的每个地方都需要两行而不是一行:首先创建一个空向量,然后调用fill()
另一个原因是,一个移动构造函数允许函数返回一个没有默认构造函数的类的实例。
struct something{
something(int i) : value(i) {}
something(const something& a) : value(a.value) {}
int value;
};
something function1(){
return something(1);
}
void function2(something& other){
other.value = 2;
}
int main(void){
// valid usage
something var1(18);
// (do something with var1 and then re-use the variable)
var1 = function1();
// compile error
something var2;
function2(var2);
}
如果您担心效率,那么您是否编写
fill()应该无关紧要
返回一个值,或将输出变量作为参数。编译器应将其优化为这两个变量中最有效的一个。如果您怀疑它不存在,则最好对其进行测量。一个原因是,使用赋值运算符可以更容易地掌握每行的操作。如果您有一个函数调用somefunction(var1,var2,var3)
,不清楚其中的一些函数是否被修改。要了解这一点,您必须实际阅读另一个函数
此外,如果必须将向量作为参数传递给fill()
,这意味着调用函数的每个地方都需要两行而不是一行:首先创建一个空向量,然后调用fill()
另一个原因是,一个移动构造函数允许函数返回一个没有默认构造函数的类的实例。
struct something{
something(int i) : value(i) {}
something(const something& a) : value(a.value) {}
int value;
};
something function1(){
return something(1);
}
void function2(something& other){
other.value = 2;
}
int main(void){
// valid usage
something var1(18);
// (do something with var1 and then re-use the variable)
var1 = function1();
// compile error
something var2;
function2(var2);
}
如果您担心效率,那么您是否编写
fill()应该无关紧要返回一个值,或将输出变量作为参数。编译器应该将其优化为这两个最有效的替代。如果你怀疑它没有,你最好测量它。你认为一个永远不需要在C++中复制对象吗?如果是这样,你就错了。一个原因是,它更容易阅读。一个函数调用somefunction(var1,var2,var3)
,不清楚其中的一些函数是否被修改。你有没有试过阅读一些关于移动构造函数和移动语义的主题?有很多信息,只要谷歌一下就可以了……只要你把vector&res
添加到fill()
,在调用fill()
之前,你需要构造一个向量。你真的认为这是一个改进吗?@Ville ValtteriTiittanen:那些没有被修改的参数应该是常量,不是吗?;-)(是的,我知道,它们通常不是。)你认为C++中永远不需要复制对象吗?如果是这样,你就错了。一个原因是,它更容易阅读。如果有函数调用<代码