当返回一个对象时,是否保证该对象被移动?

20
我知道当把一个对象按值传递给函数时,如果有移动构造函数(假设没有复制省略),那么移动构造函数总是会被调用。那么如果按值返回一个对象呢?
例如,假设我们有一个带有移动构造函数的类Foo,我们有一个函数返回一个Foo对象。
Foo g() {
    Foo f;

    // do something with f

    return f;
}

如果我们假设没有返回值优化(RVO),那么移动构造函数是否保证被调用?

更新:我想我没有清楚地表达我的意图。我只是想知道在最坏的情况下,对象被移动而不是复制。无论是RVO还是NRVO发生,我都很满意。并且我也应该说,移动构造函数和移动赋值函数没有被删除,并且已经正确实现。


是的,在返回语句中,自动存储中的局部对象会被隐式地视为 xvalues。 - ildjarn
@ildjarn:我认为只有在直接返回时才会发生这种情况。在 Stack Overflow 的某个地方,有人纠正了我说这将 f 移入函数的说法:“return do_something(f);”。 - GManNickG
@GManNickG:我没有时间去遵循标准或注意事项,因此只能留下评论而不是答案。我认为你是正确的。:-] - ildjarn
@ildjarn:其实我私下里还希望你能纠正我,说它确实会移动它(这更有意义,该死的!)。哦,算了。 :) - GManNickG
@GManNickG:确切的引用来自12.8/31:“在具有类返回类型的函数中的返回语句中,当表达式是非易失性自动对象(而不是函数或catch子句参数)的名称,并且该自动对象具有与函数返回类型相同的cv-unqualified类型时,可以通过直接将自动对象构造到函数的返回值中来省略复制/移动操作。” - Mankarse
请注意,自C++17以来,在许多情况下保证进行省略。请参见https://dev59.com/yFoT5IYBdhLWcg3w6isA - sp2danny
3个回答

9

是的。请参见[class.copy] p32。

当复制操作的省略条件被满足,或者除了源对象是函数参数之外,将被满足,且要复制的对象由lvalue指定时,首先按照对象被指定为rvalue的方式执行重载决策来选择用于复制的构造函数。如果重载决策失败,或所选构造函数的第一个参数的类型不是对该对象类型(可能带有cv限定符)的rvalue引用,则再次执行重载决策,将对象视为lvalue。[注意:无论是否会发生复制省略,都必须执行这个两阶段的重载决策。它确定要调用的构造函数,即使调用被省略,所选构造函数也必须是可访问的。——结尾注释]


3
规则是,每当允许但未发生复制省略时,如果可用,将使用移动构造函数,否则将使用复制构造函数。
具体行为由[class.copy]/32定义:
当符合复制操作省略的条件或者源对象是一个函数参数但除此之外满足省略条件,并且要被复制的对象被左值所指定时,会首先像对象被指定为右值那样执行重载解析以选择复制的构造函数。如果重载解析失败,或者所选构造函数的第一个参数类型不是对象类型(可能带有cv限定符)的右值引用,则再次执行重载解析,将对象视为左值。

2
在这种情况下,由于返回值有一个名称(f),因此将应用NRVO(命名返回值优化)。因此,仅基于措辞的技术答案是,缺少RVO不会阻止复制省略,因为NRVO仍然可以允许它。
除此之外,我认为移动/复制返回值的选择取决于Foo的定义——肯定有时候会被复制而不是移动,例如如果您已经明确删除了移动构造函数和移动赋值运算符,或者您没有定义移动构造/赋值,并且它不符合隐式合成的条件。
编辑:[回答编辑后的问题]:即使有移动构造函数,也不能保证结果将被移动。一个明显的例子是,如果您已经删除了移动赋值运算符,并且正在分配结果(而不是用它来初始化),在这种情况下,删除的移动赋值运算符将防止移动返回值。
回答您可能想到的问题,总的规则是如果可能的话会进行移动,并且只有在某些情况下才会退回到复制。

我想我在问题中没有清楚地表达我的意图。我只是想知道在最坏的情况下,我可以将对象移动而不是复制。无论是RVO还是NRVO发生,我都很高兴。我还应该说,移动构造函数和移动赋值未被删除。谢谢。 - haotang
3
@Jerry:在这段代码中有两个潜在的复制操作。第一个是从局部变量到返回语句,通常称为(N)RVO。如果有移动构造函数,这将保证不进行复制,编译器可以选择应用(N)RVO或不应用,但如果不应用,它必须使用移动构造函数。第二个潜在的复制是在调用方代码中,来自返回对象的复制,这超出了问题的范围,因为它取决于调用方代码而不是函数本身。 - David Rodríguez - dribeas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接