RVO(返回值优化)是否适用于所有对象?

时间:2011-09-29 10:47:24

标签: c++ optimization gcc

RVO(Return Value Optimization)是否保证或适用于C ++编译器(特别是GCC)中的所有对象和情境?

如果答案是“否”,那么这个类/对象的优化条件是什么?如何强制或鼓励编译器对特定的返回值执行RVO?

5 个答案:

答案 0 :(得分:41)

返回值优化可以始终应用,不能普遍应用的是命名返回值优化。基本上,为了进行优化,编译器必须知道将在构造对象的位置返回哪个对象。

在RVO(返回临时)的情况下,该条件得到满足:对象在return语句中构造,并且返回它。

对于NRVO,您必须分析代码以了解编译器是否可以知道该信息。如果对函数的分析很简单,那么编译器可能会优化它(单个return语句不包含条件,例如;同一对象的多个return语句;多个return语句,如T f() { if (condition) { T r; return r; } else { T r2; return r2; } }其中编译器知道将返回rr2 ...)

请注意,您只能在简单的情况下假设优化,具体而言,维基百科中的示例实际上可以通过足够聪明的编译器进行优化:

std::string f( bool x ) {
   std::string a("a"), b("b");
   if ( x ) return a; 
   else return b;
}

可以由编译器重写为:

std::string f( bool x ) {
   if ( x ) {
      std::string a("a"), b("b");
      return a;
   } else {
      std::string a("a"), b("b");
      return b;
   }
}

此时编译器可以知道在第一个分支a中将构造代替返回的对象,而在第二个分支中,同样适用于b。但我不指望这一点。如果代码很复杂,则假设编译器无法生成优化。

编辑:有一个案例我没有明确提到,不允许编译器(在大多数情况下,即使允许编译器,也不可能这样做)来优化掉副本从函数的参数到return语句:

T f( T value ) { return value; } // Cannot be optimized away --but can be converted into
                                 // a move operation if available.

答案 1 :(得分:5)

  

是否保证gcc编译器中所有对象的RVO(返回值优化)?

无法保证优化(尽管RVO相当可靠,但确实存在some cases that throw it off)。

  

如果答案为“否”,那么对于类/对象进行此优化的条件是什么?

一个非常故意从你身上抽象出来的实现细节。

请不要知道也不关心这个。

答案 2 :(得分:3)

对于Jesper:如果要构建的对象很大,则可能需要避免复制(或者至少非常需要)。

如果发生RVO,则避免复制,您无需再编写任何代码行。

如果没有,你必须手动完成,自己编写额外的脚手架。这可能涉及提前指定一个缓冲区,迫使你为这个空(可能无效,你可以看到这个不干净)对象和一个“构造”这个无效对象的方法写一个构造函数。

所以'如果有保证,它可以减少我的代码行。不是吗?'并不意味着马苏德是个白痴。不幸的是,RVO并不能保证。你必须测试它是否发生,如果没有,写下脚手架并污染你的设计。它不能被束缚。

答案 3 :(得分:2)

R值参考(C ++ 11的新功能)是您的问题的解决方案,允许您使用Type(Type &&r);移动构造函数)明确地,而不是Type(const Type &r)复制构造函数)。

例如:

class String {
  public:    
    char *buffer;

    String(const char *s) { 
      int n = strlen(s) + 1;
      buffer = new char[n];
      memcpy(buffer, s, n);
    }

    ~String() { delete [] buffer; }

    String(const String &r) { 
      // traditional copy ...
    }

    String(String &&r) {
      buffer = r.buffer; // O(1), No copying, saves time.
      r.buffer = 0;
    }
};

String hello(bool world) {
  if (world) {
    return std::move( String("Hello, world.") );
  } else {
    return std::move( String("Hello.") );
  }
}

int main() {
  String foo = hello();
  std::cout <<foo.buffer <<std::endl;
}

这不会触发复制构造函数

<强> EDIT1

代码

return std::move( String(...) );

可以缩短为

return String(...);

由于临时对象默认为R-value-ref。

答案 4 :(得分:0)

我没有是或否答案,但你说如果你正在寻找的优化得到保证,你可以编写更少的代码行。

如果您编写需要编写的代码,程序将始终有效,如果有优化,它将更快地运行。如果确实存在优化“填补空白”的逻辑而不是代码的机制并使其工作,或彻底改变逻辑的情况,这似乎是我想要修复的错误而不是我希望依赖或利用的实现细节。