【问题标题】:Is an object guaranteed to be moved when it is returned?对象返回时是否保证被移动?
【发布时间】:2012-06-20 17:28:45
【问题描述】:

我知道当通过值传递一个对象给函数时,如果有一个移动构造函数,总是会调用它,假设没有复制省略。按值返回对象呢?

例如,假设我们有一个类Foo,它有一个移动构造函数,我们有一个函数返回一个Foo 对象。

Foo g() {
    Foo f;

    // do something with f

    return f;
}

如果我们假设没有 RVO,是否保证调用移动构造函数?

更新:我想我没有清楚地表明我的意图。我只是想知道在最坏的情况下我可以让对象移动而不是复制。 RVO 或 NRVO 发生,我很高兴。而且我还应该说move构造函数和move赋值没有被删除,并且是正确实现的。

【问题讨论】:

  • 是的,自动存储中的本地对象在 return 语句中被隐式视为 xvalues。
  • @ildjarn:我认为只有直接退货。在某处,我被纠正说这将f 移动到函数中:return do_something(f);
  • @GManNickG :我没有时间了解标准或警告,因此是评论而不是答案。我认为你是对的。 :-]
  • @ildjarn:我其实暗地里希望你能纠正我说它确实移动了它(这更有意义该死!)。那好吧。 :)
  • @GManNickG:确切的引用来自 12.8/31:“在具有类返回类型的函数的返回语句中,当表达式是非易失性自动对象的名称时(除了函数或 catch 子句参数)具有与函数返回类型相同的 cv 非限定类型,则可以通过将自动对象直接构造到函数的返回值中来省略复制/移动操作。

标签: c++ c++11 return-value move-semantics


【解决方案1】:

是的。参见 [class.copy] p32

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

【讨论】:

    【解决方案2】:

    在这种情况下,由于返回值有一个名称 (f),因此将应用 NRVO(命名返回值优化)。

    因此,仅基于措辞的技术答案是,缺少 RVO 不会阻止复制省略,因为 NRVO 仍然允许它。

    过去,我相信返回值的移动/复制之间的选择可以/将取决于 Foo 的定义 - 肯定有时间它会被复制而不是移动,例如如果您已明确删除移动构造函数和移动赋值运算符,或者你没有定义移动构造/赋值,它不适合隐式合成它们。

    编辑:[响应编辑的问题]:拥有移动构造函数仍然不能保证结果会被移动。一个明显的例子是,如果您删除了移动赋值运算符,并且正在分配结果(而不是使用它来初始化)。在这种情况下,删除的移动赋值运算符将阻止移动返回值。

    不过,为了回答您可能遇到的问题,一般规则是,如果可能,将进行移动,并且当且仅当某事阻止结果被移动时,它才会回退到复制。

    【讨论】:

    • 我想我没有在问题中清楚地表明我的意图。我只是想知道在最坏的情况下我可以让对象移动而不是复制。 RVO 或 NRVO 发生,我很高兴。而且我还应该说移动构造函数和移动赋值没有被删除。谢谢。
    • @Jerry:该​​代码中有两个潜在的副本。第一种是从局部变量到return语句,通常称为(N)RVO。如果存在移动构造函数,则保证不会复制,编译器可以应用 (N)RVO 或不应用,但如果没有,它必须 移动构造。第二个潜在副本位于调用方,来自返回的对象,这超出了问题的范围,因为它取决于调用方代码,而不是函数。
    【解决方案3】:

    规则是,只要允许复制省略但不发生复制省略,如果移动构造函数可用,将使用移动构造函数,否则将使用复制构造函数。

    确切的行为由[class.copy]/32定义:

    当满足或将满足省略复制操作的条件时,除了源对象是函数参数的事实,并且要复制的对象由左值指定时,重载决策选择构造函数首先执行复制,就好像对象由右值指定一样。如果重载解析失败,或者如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是 cv 限定的),则再次执行重载解析,将对象视为左值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-12
      • 2013-06-13
      • 2023-03-04
      • 2014-12-06
      • 1970-01-01
      • 1970-01-01
      • 2018-08-01
      相关资源
      最近更新 更多