Let's assume an exemplary function, that can't be changed (e.g. because its linked against without the source code at hand), and it can't be subject to NRVO. Something like the following:
auto f()
{
std::vector<int> v1 { 3, 2, 1 };
std::vector<int> v2 { 2, 1, 3 };
/* RVO impossible. */
return someRuntimeCondition ? v1 : v2;
}
On the caller's side,
auto&& vec = f(); /* Extend lifetime to current scope. */
std::sort(vec.begin(), vec.end()); /* Const-lvalue ref. was no option. */
This should avoid move-constructing a local std::vector
from the return value. But I don't see such snippets frequently. Am I wrong in my reasoning about the above snippets or are functions like f()
so rare that relying on RVO is the more idiomatic apprach?
I am asking to understand how one should bind the return value of f()
under the assumption that RVO is not applicable.
Since C++17, if a function returns by value then the following two options are exactly the same:
auto a = f();
auto&& a = f();
except for the result of decltype(a)
. The result object is named by a
in both cases, and the result object is directly initialized by the return
statement in the function.
Prior to C++17 the result object was always a temporary and auto a = f();
copy-initialized a
from the temporary, with this operation being optionally elidable.
Some people did use the practice of doing auto&& a = f();
to defend against compilers which did not implement optional copy elision. There are pros and cons to this.
Personally I use the former because I'm using C++17 , and even before that I only used compilers which did maximal copy elision. I'm not actually aware of any compiler that did not do copy elision here, except for old versions of MSVC running in Debug Mode.
But I can imagine people writing code bases for clients who use old compilers might feel the need to mangle their code for performance reasons.