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 ?
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.
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.
Compilers often perform Named Return Value Optimization (NRVO) in such cases, but it is not guaranteed.
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 ...
"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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With