Is RVO (Return Value Optimization) applicable for all objects?

C++OptimizationGcc

C++ Problem Overview


Is RVO (Return Value Optimization) guaranteed or applicable for all objects and situations in C++ compilers (specially GCC)?

If answer is "no", what are the conditions of this optimization for a class/object? How can I force or encourage the compiler to do a RVO on a specific returned value?

C++ Solutions


Solution 1 - C++

Return Value Optimization can always be applied, what cannot be universally applied is Named Return Value Optimization. Basically, for the optimization to take place, the compiler must know what object is going to be returned at the place where the object is constructed.

In the case of RVO (where a temporary is returned) that condition is trivially met: the object is constructed in the return statement, and well, it is returned.

In the case of NRVO, you would have to analyze the code to understand whether the compiler can know or not that information. If the analysis of the function is simple, chances are that the compiler will optimize it (single return statement that does not contain a conditional, for example; multiple return statements of the same object; multiple return statements like T f() { if (condition) { T r; return r; } else { T r2; return r2; } } where the compiler knows that r or r2 will be returned...)

Note that you can only assume the optimization in simple cases, specifically, the example in wikipedia could actually be optimized by a smart enough compiler:

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

Can be rewritten by the compiler into:

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

And the compiler can know at this time that in the first branch a is to be constructed in place of the returned object, and in the second branch the same applies to b. But I would not count on that. If the code is complex, assume that the compiler will not be able to produce the optimization.

EDIT: There is one case that I have not mentioned explicitly, the compiler is not allowed (in most cases even if it was allowed, it could not possibly do it) to optimize away the copy from an argument to the function to the return statement:

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

Solution 2 - C++

> Is RVO (Return Value Optimization) guaranteed for all objects in gcc compilers?

No optimisation is ever guaranteed (though RVO is fairly dependable, there do exist some cases that throw it off).

> If answer is "no", what is the conditions of this optimization for a class/object?

An implementation detail that's quite deliberately abstracted from you.

Neither know nor care about this, please.

Solution 3 - C++

To Jesper: if the object to be constructed is big, avoiding the copy might be necessary (or at the very least highly desirable).

If RVO happens, the copy is avoided and you need not write any more lines of code.

If it doesn't, you'll have to do it manually, writing extra scaffolding yourself. And this will probably involve designating a buffer in advance, forcing you to write a constructor for this empty (probably invalid, you can see how this is not clean) object and a method to ‘construct’ this invalid object.

So ‘It can reduce my lines of code if it's guaranteed. Isn't it?’ does not mean that Masoud is a moron. Unfortunately for him however, RVO is not guaranteed. You have to test if it happens and if it doesn't, write the scaffolding and pollute your design. It can't be herped.

Solution 4 - C++

Move semantics (new feature of C++11) is a solution to your problem, which allows you to use Type(Type &&r); (the move constructor) explicitly, instead of Type(const Type &r) (the copy constructor).

For example:

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 String("Hello, world.");
  } else {
    return String("Hello.");
  }
}

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

And this will not trigger the copy constructor.

Solution 5 - C++

I don't have a yes or no answer, but you say that you can write fewer lines of code if the optimization you're looking for is guaranteed.

If you write the code you need to write, the program will always work, and if the optimization is there, it will work faster. If there is indeed a case where the optimization "fills in the blank" in the logic rather than the mechanics of the code and makes it work, or outright changes the logic, that seems like a bug that I'd want fixed rather than an implementation detail I'd want to rely on or exploit.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmasoudView Question on Stackoverflow
Solution 1 - C++David Rodríguez - dribeasView Answer on Stackoverflow
Solution 2 - C++Lightness Races in OrbitView Answer on Stackoverflow
Solution 3 - C++Anonymous CowardView Answer on Stackoverflow
Solution 4 - C++RnMssView Answer on Stackoverflow
Solution 5 - C++JesperView Answer on Stackoverflow