复制省略,std :: move和链接的函数调用

时间:2018-08-01 20:27:00

标签: c++ optimization move-semantics copy-elision

我一直在研究当复制省略没有直接分配给左值并且可能被链接或沿途使用时如何表现,但没有找到任何具体答案。

对于初学者而言,我理解在以下示例中会发生NRVO,并且直接在目标变量中构造返回值:

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

1 个答案:

答案 0 :(得分:2)

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

将std :: move添加到返回类型没有帮助。您已经按值返回,因此您已经有一个右值。我不应该使用std :: move强制将其强制转换。 我不知道细节,但是,在某些情况下,添加它会影响性能。

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

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

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

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

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