c++stlc++17return-value-optimization

Why C++ does not perform RVO to std::optional?


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


Solution

  • 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.