为什么从NRVO中排除了按值参数?

Joh*_*itb 38 c++ return-value-optimization c++11

想像:

S f(S a) {
  return a;
}
Run Code Online (Sandbox Code Playgroud)

为什么不允许别名a和返回值槽?

S s = f(t);
S s = t; // can't generally transform it to this :(
Run Code Online (Sandbox Code Playgroud)

如果复制构造函数S具有副作用,则规范不允许此转换.相反,它需要至少两个副本(一个来自ta,一个来自a于返回值,另一个是返回值s,且只有最后一个可以省略.请注意,我写了= t上述表示的副本的事实的t到f的a,这仍然是在移动的副作用存在强制性的唯一拷贝/拷贝构造函数).

这是为什么?

Nic*_*las 20

这就是为什么copy elision对参数没有意义的原因.它实际上是关于编译器级别的概念的实现.

复制elision的工作原理是基本上构建返回值.该值不会被复制出来; 它直接在预定目的地创建.调用者为预期的输出提供空间,因此最终是调用者提供了省略的可能性.

函数内部需要做的所有操作都是为了忽略副本,而是在调用者提供的位置构造输出.如果函数可以执行此操作,则会获得copy elision.如果函数不能,则它将使用一个或多个临时变量来存储中间结果,然后将其复制/移动到调用者提供的位置.它仍然就地构建,但输出的构造通过复制发生.

因此,特定功能之外的世界不必知道或关心某个功能是否能够完成.具体而言,函数的调用者不必知道函数的实现方式.它没有做任何不同的事情; 它是功能本身决定是否可以省略.

调用者还提供值参数的存储.当您调用时f(t),调用者会创建副本t并将其传递给f.类似地,如果S是从a隐式构造的int,那么f(5)将从S5 构造一个并将其传递给f.

这一切都是由来电者完成的.被叫者不知道或不关心它是变量还是临时的; 它只是给出了一堆堆栈内存(或寄存器或其他).

现在记住:copy elision有效,因为被调用的函数将变量直接构造到输出位置.因此,如果您试图忽略值参数的返回,则value参数的存储也必须是输出存储本身.但请记住:调用者为参数和输出提供存储.因此,为了省略输出副本,调用者必须将参数直接构造到输出中.

要做到这一点,现在调用者需要知道它调用的函数将忽略返回值,因为如果返回参数,它只能将参数直接粘贴到输出中.这在编译器级别通常是不可能的,因为调用者不一定具有该函数的实现.如果函数是内联的,那么也许它可以工作.但否则没有.

因此,C++委员会并没有考虑这种可能性.

  • 在C++ 03和C++ 11之间,委员会将"表达式是非易失性自动对象的名称"更改为"表达式是非易失性自动对象的名称(除了函数或捕获之外)子句参数)".因此,委员会"并不愿意考虑这种可能性"的情况并非如此.它在C++ 03中被允许(可能是偶然的),然后委员会在C++ 11中禁止它. (6认同)
  • 追溯看起来是正确的;)是否有任何编译器从参数中实现NRVO可能的意外限制?由于尼科尔给出的令人信服的理由,我可能不会_asume_.在C++ 11修改措辞之后(尽管没有真正的讨论),他们不允许这样做.所以实际上,也许每个_intention_的C++ <11,到目前为止 - 这看起来像是一个正确的答案.我确信使用先进的LTO等,编译器可以在这种情况下执行copy-elision,但似乎委员会不想通过将其定义为明确的事情来思考. (2认同)

Öö *_*iib 1

从 t 到 a 删除副本是不合理的。该参数被声明为可变的,因此需要进行复制,因为预计会在函数中对其进行修改。

从返回值我看不出任何复制的理由。也许这是某种疏忽?按值参数感觉就像函数体内的局部变量......我看不出有什么区别。