0

我有以下代码:

SomeClass func()
{
    SomeClass someObject;
    someObject.mutate("some text");
    return someObject;
}

int main()
{
    func();
    return 0;
}

SomeClass 仅在构造函数中记录某些内容,因此我可以验证正在调用的内容。

通过发布版本,我有以下输出:

default constructor

由于复制/移动省略,这是有道理的。我想关闭返回值优化。使用调试版本,我有以下输出:

default constructor
move constructor

我认为我可以安全地假设 NRVO si 关闭。我真的很想知道为什么要调用移动构造函数而不是复制构造函数。我的(可能是错误的)理解是,由于 func 中的 someObject 是一个左值,因此应该使用复制构造函数而不是移动构造函数来初始化返回对象。

我错过了什么?有人可以指出标准中阐明该案例的段落吗?

4

2 回答 2

2

cppreference.com获取return [expression];

如果[expression]是一个左值表达式,它是在主体中声明的自动存储持续时间对象的(可能是带括号的)名称,或者作为最内层封闭函数或 lambda 表达式的参数,则重载决策以选择用于初始化返回的构造函数value 被执行两次:第一次好像[expression]是一个右值表达式(因此它可以选择移动构造函数),如果没有合适的转换可用,或者如果所选构造函数的第一个参数的类型不是对对象的右值引用类型(可能是 cv 限定的),第二次执行重载解析,并被[expression]视为左值(因此它可以选择复制构造函数引用非 const)。

简而言之,从 C++11 开始,return如果可能,语句将更喜欢使用移动构造函数并回退到复制构造函数。

于 2018-12-11T20:54:39.053 回答
0

同样来自 C++11 标准,12.8 复制和移动对象,第 285 页,第 32 项:

当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定之外,选择复制的构造函数的重载决策是首先执行好像对象是由右值指定的。如果重载决议失败,或者如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是 cv 限定的),则再次执行重载决议,将对象视为左值。[注意:无论是否会发生复制省略,都必须执行此两阶段重载解析。它确定如果不执行省略则要调用的构造函数,并且即使调用被省略,所选构造函数也必须是可访问的。——尾注]

于 2018-12-11T21:16:59.847 回答