首页 > 解决方案 > 复制省略、std::move 和链式函数调用

问题描述

我一直在研究复制省略在没有直接分配给左值并且可能被链接或在路上使用时的行为,但还没有找到任何具体的答案。

对于初学者,我理解NRVO出现在下面的例子中,返回值是直接在destination变量中构造的:

Type MakeType() {
  Type type;
  // ...
  return type;
}

Type a = MakeType();

但是,假设我们有另一个函数接受 Type 作为参数:

Type MakeComplexType(/*some signature*/ param_type) {
  Type complex_type = param_type
  // ...
  return complex_type;
}

我们称之为:

Type t = MakeComplexType(MakeType());
  1. 是否可以将复制省略一直链接到t
  2. 如果不是,我们是否可以std::move策略性地使用,例如在函数调用本身上std::move(MakeType()),以避免不必要的复制?
  3. 的签名应该是什么才能param_type使上述分配t最有效?

标签: c++optimizationmove-semanticscopy-elision

解决方案


复制省略是编译器用来防止不需要的复制的技术。基本上,它在函数外部预先分配内存并将其传递给使用。如果您是临时的,它将在堆栈上。

将 std::move 添加到返回类型没有帮助。您已经按值返回,因此您已经有一个右值。如果我没有操作,使用 std::move 将其转换为没有右值。但是,我不知道细节,在某些情况下添加它会损害性能。

关注 2:在函数调用中添加 std::move 只有在由非常量引用返回时才会产生副作用。在这些情况下,您很可能编写了一个错误,因为原始文件将被移走。

对于数字 3:我最喜欢使用f(Arg &&a),因为这要求所有调用者都传递右值。如果性能不太重要,例如:您在分析中没有找到它。值参数(一些调用者可以复制)甚至 const-reference 可能会这样做(函数不能触摸参数,所以应该复制)。

正如评论所指出的,函数的实现也应该写成auto result = std::move(a)你的参数不能从 NRVO 中受益。

最新版本的 Clang 对何时应使用 std::move 以及何时删除它有很好的警告。我建议启用它们。GCC 可能有一些类似的警告,但我不是最新的。

简而言之:您的原始代码是使用和信任您的编译器的最佳版本,如果它对此有警告。


推荐阅读