Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is RVO garanteed to apply / does apply with C++20 compilers

The C++ core guidelines states that

F.20: For “out” output values, prefer return values to output parameters

But then gives the following exception:

struct Package {      // exceptional case: expensive-to-move object
    char header[16];
    char load[2024 - 16];
};

Package fill();       // Bad: large return value
void fill(Package&);  // OK

Isn't it supposed to be a case where the return value optimization kicks in ? Is RVO prevented in this case ? Or still not as efficient as passing by reference ? Or is it that some compilers don't manage to do it ?

More generally, when should I rely on the compiler optimizing return values as efficiently as the pass-by-reference technique ?

like image 329
Bérenger Avatar asked Oct 06 '19 22:10

Bérenger


People also ask

How RVO works?

The neat thing about RVO is that it makes returning objects free. It works via allocating memory for the to-be-returned object in the caller's stack frame. The returning function then uses that memory as if it was in its own frame without the programmer knowing / caring.

How does RVO work C++?

In the context of the C++ programming language, return value optimization (RVO) is a compiler optimization that involves eliminating the temporary object created to hold a function's return value. RVO is allowed to change the observable behaviour of the resulting program by the C++ standard.

Is NRVO guaranteed?

Compilers often perform Named Return Value Optimization (NRVO) in such cases, but it is not guaranteed.

Under what conditions copy elision is performed?

In a return statement or a throw-expression, if the compiler cannot perform copy elision but the conditions for copy elision are met or would be met, except that the source is a function parameter, the compiler will attempt to use the move constructor even if the object is designated by an lvalue; see return statement ...


Video Answer


2 Answers

"Plain" RVO (i.e., returning a prvalue or "temporary" in common parlance) is guaranteed in C++17 and well-supported even before that.

NRVO (i.e., returning a local variable) can be finicky and is not guaranteed, and if it's not performed then you get a move instead. If your move is expensive, you may want to avoid that.

In the example, there's a decent chance that fill needs to use the latter.

like image 133
T.C. Avatar answered Oct 18 '22 18:10

T.C.


Or still not as efficient as passing by reference ?

If RVO applies, then it is equally efficient to return a value, as it is to use an output reference.

Is RVO prevented in this case?

No. Being "big" does not prevent the object from being RVO'd.

When is RVO garanteed to apply / does apply with C++20 compilers

A case where it does not apply:

... A return statement can involve an invocation of a constructor to perform a copy or move of the operand if it is not a prvalue or if its type differs from the return type of the function.

So, it depends on the implementation of the function whether copy-elision is guaranteed.

The guidelines indeed fail to explain why the recommendation should be followed.

Note that the exception says:

Exceptions

If a type is expensive to move (e.g., array<BigPOD>), consider allocating it on the free store and return a handle (e.g., unique_ptr), or passing it in a reference to non-const target object to fill (to be used as an out-parameter).

The highlighted suggestion in the exception makes more sense to me. It makes it clear that the object is too big for stack, and thus reduces the chance stack overflows.

like image 45
eerorika Avatar answered Oct 18 '22 19:10

eerorika