什么时候应该在函数返回值上使用 std::move?

新手上路,请多包涵

在这种情况下

struct Foo {};
Foo meh() {
  return std::move(Foo());
}

我很确定此举是不必要的,因为新创建的 Foo 将是一个 xvalue。

但是在这样的情况下呢?

 struct Foo {};
Foo meh() {
  Foo foo;
  //do something, but knowing that foo can safely be disposed of
  //but does the compiler necessarily know it?
  //we may have references/pointers to foo. how could the compiler know?
  return std::move(foo); //so here the move is needed, right?
}

那里需要搬家,我想?

原文由 user2015453 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.5k
2 个回答

对于 return std::move(foo); move 是多余的,因为 12.8/32:

当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定之外,选择复制的构造函数的重载决策是首先执行好像对象是由一个右值指定的。

return foo; 是 NRVO 的情况,所以允许复制省略。 foo 是一个左值。因此,为从 foo 到返回值 meh 的“复制”选择的构造函数如果存在,则必须是移动构造函数。

添加 move 确实有潜在的影响:它可以防止移动被忽略,因为 return std::move(foo); 符合 NRVO 的条件。

据我所知,12.832 列出了左值的副本可以被移动替换的 唯一 条件。通常不允许编译器在复制后检测到未使用的左值(例如使用 DFA),并主动进行更改。我在这里假设两者之间存在可观察到的差异——如果可观察到的行为相同,则适用“as-if”规则。

因此,要回答标题中的问题,当您希望它被移动并且它不会被移动时,请在返回值上使用 std::move 。那是:

  • 你希望它被移动,并且
  • 它是一个左值,并且
  • 它不符合复制省略的条件,并且
  • 它不是按值函数参数的名称。

考虑到这很繁琐,而且移动 通常 很便宜,您可能想说在非模板代码中您可以稍微简化一下。在以下情况下使用 std::move

  • 你希望它被移动,并且
  • 它是一个左值,并且
  • 你不必担心它。

通过遵循简化的规则,您会牺牲一些移动省略。对于像 std::vector 这样的类型,移动起来很便宜,你可能永远不会注意到(如果你注意到了,你可以优化)。对于像 std::array 类的移动成本很高的类型,或者对于您不知道移动是否便宜的模板,您更有可能担心它。

原文由 Steve Jessop 发布,翻译遵循 CC BY-SA 3.0 许可协议

在返回值上,如果返回表达式直接引用本地左值的名称(即此时为 xvalue),则不需要 std::move 。另一方面,如果返回表达式 不是 标识符,它不会自动移动,例如,在这种情况下,您需要显式 std::move

 T foo(bool which) {
   T a = ..., b = ...;
   return std::move(which? a : b);
   // alternatively: return which? std::move(a), std::move(b);
}

直接返回命名局部变量或临时表达式时,应避免显式 std::move 。在这些情况下,编译器 _必须_(并且将来会)自动移动,并且添加 std::move 可能会影响其他优化。

原文由 David Rodríguez - dribeas 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题