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++中永远不需要复制对象吗?如果是这样,你就错了。一个原因是,它更容易阅读。如果有函数调用<代码