I am wondering why C++ does not perform RVO to std::optional<T>
when returning T
.
I.e.,
struct Bar {};
std::optional<Bar> get_bar()
{
return Bar{}; // move ctor of Bar called here
// instead of performing RVO
}
Bar get_bar2()
{
return Bar{}; // NO move ctor called
// RVO performed
}
std::optional<Bar> get_bar_rvo()
{
std::optional<Bar> opt;
opt.emplace();
return opt; // NO move ctor called
// ROV performed
}
In this above case, get_bar2
performs RVO while get_bar
does not.
With a little bit more hint, the compiler is able to optimize get_bar_rvo
, but the code get longer and annoying.
From the reference, I understand that get_bar
does not meet the requirement of "Mandatory elision of copy/move operations"
In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:
Since std::optional<T>
and T
are not the same class type, so RVO is not mandatory.
However, I think that performing RVO to std::optional<T>
should be very easy and would be very useful, without the need to manually write the longer code as get_bar_rvo
.
Why my compile fails to recognize and optimize the get_bar
just like get_bar2
?
Environments: MacOS
Apple clang version 13.1.6 (clang-1316.0.21.2.5)
Target: arm64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
Compiled with -std=c++17 -O3
get_bar_rvo
does not perform RVO, it performs NRVO (Named Return Value Optimization). This is not guaranteed.
For get_bar
, instead of constructing a Bar
yourself, you can leave that to std::optional
using std::make_optional
or its in-place constructor (6):
std::optional<Bar> get_bar()
{
return std::make_optional<Bar>();
// or return std::optional<Bar>(std::in_place);
}
This performs RVO as expected.