我知道当按值将对象传递给函数时,如果有的话,总是调用移动构造函数,假设没有复制省略。如何按值返回对象?
例如,假设我们有一个具有移动构造函数的类Foo
,并且我们有一个返回Foo
对象的函数。
Foo g() {
Foo f;
// do something with f
return f;
}
如果我们假设没有RVO,那么移动构造函数是否可以保证被调用?
更新:我想我没有明确表达我的意图。我只是想知道我可以在最坏的情况下移动对象而不是复制。无论是RVO还是NRVO,我很高兴。我还应该说移动构造函数和移动赋值不会被删除并且已正确实现。
答案 0 :(得分:6)
是。见[class.copy] p32
当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定,重载决策以选择构造函数首先执行复制,就好像对象是由右值指定的一样。如果重载解析失败,或者所选构造函数的第一个参数的类型不是rvalue引用 对象的类型(可能是cv-qualified),再次执行重载决策,将对象视为左值。 [注意:无论是否发生复制省略,都必须执行此两阶段重载决策。如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数。 - 结束记录]
答案 1 :(得分:2)
在这种情况下,由于返回值具有名称(f
),因此它将是适用的NRVO(命名返回值优化)。
因此,仅基于措辞的技术答案是,由于NRVO仍然可以允许,因此RVO的缺失不会阻止复制省略。
过去,我相信返回值的移动/复制之间的选择可以/将取决于Foo的定义 - 肯定会被复制而不是移动,例如,如果你已经明确删除移动构造函数和移动赋值运算符,或者您尚未定义移动构造/赋值,并且它们不能隐式合成它们。
编辑:[回复已编辑的问题]:拥有移动构造函数仍不能保证结果将被移动。一个明显的例子是,如果您删除了移动赋值运算符,并且正在分配结果(而不是使用它来初始化)。在这种情况下,删除的移动赋值运算符将阻止移动返回值。
为了回答你可能已经得到的东西,一般规则是如果可能的话,移动将会完成,当且仅当某些事情阻止了移动结果时,它才会回归复制。
答案 2 :(得分:1)
规则是,只要允许复制省略但未发生复制省略,将使用移动构造函数(如果可用),否则将使用复制构造函数。
确切的行为由[class.copy]/32
:
当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定,重载决策以选择构造函数首先执行复制,就好像对象是由右值指定的一样。如果重载决策失败,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值。