复制省略、std::move 和链式函数调用

Copy elision, std::move, and chained function calls

我一直在研究复制省略在未直接分配给左值并且可能链接或在以后使用时的行为方式,但尚未找到任何具体答案。

对于初学者,我理解NRVO发生在下面的例子中,return值直接在目标变量中构造:

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 的分配最有效?

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

将 std::move 添加到 return 类型没有帮助。您已经 return 按值计算,因此您已经有一个右值。使用 std::move 将其设为 no 右值应该是空操作。 我不知道细节,但是,在某些情况下,添加它会损害性能。

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

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

如注释所示,函数的实现也应写成 auto result = std::move(a),因为您的参数无法从 NRVO 中受益。

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

简而言之:您的原始代码是最好的版本,如果它有关于此的警告,请相信您的编译器。