对未命名对象的RVO(返回值优化)是否具有普遍保证的行为?

时间:2011-12-07 05:37:21

标签: c++ standards pass-by-value return-value-optimization

This question处于不同的方面(也限于gcc)。我的问题仅适用于未命名对象。允许Return Value Optimization 更改生成的程序的可观察行为 。这似乎也在标准中提到。

然而,这个"允许" 术语令人困惑。这是否意味着每个编译器都会发生RVO 保证。由于RVO下面的代码改变了它的可观察行为:

#include<iostream>
int global = 0;
struct A {
  A(int *p) {}
  A(const A &obj) { ++ global; }
};

A foo () {  return A(0); }  // <--- RVO happens
int main () {
  A obj = foo(); 
  std::cout<<"global = "<<global<<"\n"; // prints 0 instead of 2
}

该程序是否假设为所有实现打印global = 0,而不管编译器优化和方法大小为foo

3 个答案:

答案 0 :(得分:4)

根据标准,程序可以打印0,1或2.C ++ 11中的特定段落是12.8p31,以:

开头
  

当满足某些条件时,允许实现省略类对象的复制/移动构造,即使该对象的复制/移动构造函数和/或析构函数具有副作用。

请注意,两个副本元素都不属于 as-if 规则中的优化(这要求程序的行为与同一程序的行为一致 as-如果没有进行任何优化)。该标准明确允许实现生成不同的可观察行为,程序员可以让您的程序不依赖于该程序(或接受所有三种可能的结果)。

注2:在任何答案中都没有提到1,但这是可能的结果。有两个可能的副本,从函数中的局部变量到返回的对象到main中的对象,编译器可以忽略所有,一个或两个副本生成所有三个可能的输出。

答案 1 :(得分:2)

迂腐地说明其实施的定义。现代编译器足够聪明,可以进行这种优化。

但是不能保证这些行为在实现中完全相同。这就是实现定义行为的全部内容。

“允许”在此上下文中表示012是符合标准的输出。

答案 2 :(得分:2)

无法保证。如果你试图连贯地写出这样的保证,你就会发现不可能这样做。

例如,请考虑以下代码:

std::string f() {
  std::string first("first");
  std::string second("second");
  return FunctionThatIsAlwaysFalse() ? first : second;
}

函数FunctionThatIsAlwaysFalse始终返回false,但您只能告诉您是否进行模块间优化。标准是否要求每个编译器进行模块间优化,以便在这种情况下可以使用RVO?那会怎么样?或者,当需要进行模块间优化时,是否应该禁止任何编译器使用RVO?那会怎么样?怎么能阻止那些足够聪明的编译器看到RVO可以做到这一点而那些不是没有做过的?

标准列表是否需要每个优化编译器都支持RVO?在其他情况下是否应该禁止RVO?那会不会失败优化编译器的重点?

那么编译器认为RVO会降低性能的情况又如何呢?是否应该要求编译器进行优化,它认为是坏的?例如:

if(FunctionCompilerKnowsHasNoSideEffectsAndThinksMostlyReturnsFalse())
  return F(3); // Here RVO is a pessimization
else
{
 Foo j=F(3);
 return Foo(j);
}

这里,如果编译器不需要执行RTO,如果可以避免if和函数调用,因为没有RTO,代码在两半中都是相同的。你是否应该强制编译器进行优化,它认为会让事情变得更糟?为什么呢?

真的没办法做出这样的保证工作。