Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/142.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/macos/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 按值返回副本,而不是移动_C++_C++11 - Fatal编程技术网

C++ 按值返回副本,而不是移动

C++ 按值返回副本,而不是移动,c++,c++11,C++,C++11,为什么这个程序调用复制构造函数而不是移动构造函数 class Qwe { public: int x=0; Qwe(int x) : x(x){} Qwe(const Qwe& q) { cout<<"copy ctor\n"; } Qwe(Qwe&& q) { cout<<"move ctor\n"; } }; Qwe foo(int x) { Qw

为什么这个程序调用复制构造函数而不是移动构造函数

class Qwe {
public:
    int x=0;
    Qwe(int x) : x(x){}
    Qwe(const Qwe& q) {
        cout<<"copy ctor\n";
    }
    Qwe(Qwe&& q) {
        cout<<"move ctor\n";
    }    
};

Qwe foo(int x) {
    Qwe q=42;
    Qwe e=32;
    cout<<"return!!!\n";
    return q.x > x ? q : e;
}

int main(void)
{
    Qwe r = foo(50);
}
返回q.x>x?q:e用于禁用nrvo。当我将它包装在
std::move
中时,它确实被移动了。但是在“C++之旅”中,作者说,当move C'tor可用时,必须调用它


我做错了什么?

您编写函数的方式不允许出现复制/移动省略。用移动替换副本的要求如下:

:

在以下复制初始化上下文中,可能会执行移动操作 用于替代复制操作:

  • 如果return语句中的表达式是使用automatic 正文中声明的存储持续时间或 最里面的封闭函数或lambda表达式的参数声明子句
选择副本构造函数的重载解析是第一个 像对象由右值指定一样执行。如果第一个 重载解析失败或未执行,或者 所选构造函数的第一个参数不是右值引用 对于对象的类型(可能是cv限定),重载解析为 再次执行,将对象视为左值

上面的内容来自C++17,但C++11的措辞基本相同。条件运算符不是为函数范围内的对象命名的id表达式

在您的特定情况下,id表达式类似于
q
e
。您需要在该范围内命名一个对象。条件表达式不符合命名对象的条件,因此它必须预先生成副本


如果你想在一堵难懂的文本墙上锻炼你的英语理解能力,那么这就是C++11的编写方法。需要一些努力才能看到IMO,但与上面澄清的版本相同:

当满足某些条件时,允许实现省略 类对象的复制/移动构造,即使复制/移动 对象的构造函数和/或析构函数有副作用。[...] 这种复制/移动操作的省略称为复制省略 在以下情况下允许(可合并为 消除多个副本):

  • 在具有类返回类型的函数中的return语句中,当表达式是非易失性自动对象(其他)的名称时 比函数或catch子句参数)具有相同的 cv不合格类型作为函数返回类型,复制/移动 通过直接构造自动对象,可以省略操作 输入函数的返回值
当满足或将满足省略复制操作的条件时 met save,因为源对象是函数参数, 要复制的对象由左值重载指定 首先执行为副本选择构造函数的解析 好像对象是由右值指定的。If重载解析 失败,或者如果选择的第一个参数的类型 构造函数不是对对象类型的右值引用(可能是 cv合格),考虑到 对象作为左值


讲故事的人没有回答这个问题:为什么不叫搬家人?(而不是:为什么没有拷贝省略?)

我的建议是:只有在以下情况下才会调用move c'tor:

  • 不执行复制省略(RVO)。使用三元运算符确实是防止复制省略的一种方法。让我指出,
    返回(0,q)q
    ,则code>是一种更简单的方法。它使用(in-)著名的逗号运算符。可能
    返回((q))也可能有用,但我不是一个足够的语言律师,不能肯定地说
  • return
    的参数是一个右值。这可能是一个临时值(更准确地说,是一个PR值),但它们也有资格进行复制省略。因此,
    return
    的参数必须是一个xvalue,例如
    std::move(q)
    ,如果您想确保调用move c'tor
另见:

您的示例还有一些技术细节:

  • q
    e
    Qwe
    类型的对象
  • q.x>x?q:e
    是类型为
    Qwe
    的左值表达式。这是因为表达式
    q
    e
    Qwe
    类型的左值。三元运算符只选择其中一个
  • std::move(q.x>x?q:e)
    是类型为
    Qwe
    的xvalue表达式。std::move
只需将左值转换为x值。顺便说一句,
q.x>x?std::move(q):std::move(e)
也可以
  • return q.x>x中调用复制任务?q:e
    因为它可以用类型为
    Qwe
    的左值调用(constness是可选的),而另一方面,move c'tor不能用左值调用,因此从候选集中删除
  • 更新:通过更深入的方式来评论评论…这是C++的一个非常令人困惑的方面。p> 从概念上讲,在C++98中,按值返回对象意味着返回对象的副本,因此将调用copy C'tor。然而,该标准的作者认为,编译器应该可以自由地执行优化,以便在适当的情况下可以省去这一潜在的昂贵副本(例如容器)

    这种复制省略意味着,被调用方不是在一个地方创建对象,然后将其复制到调用方控制的内存地址,而是直接在调用方控制的内存中创建对象。因此,只有