Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ return value optimization, multiple unnamed returns

Tags:

c++

rvo

Let's consider these two functions :

// 1. Multiple returns of the same named object
string f() {
    string s;
    if (something())
        return s.assign(get_value1());
    else
        return s.assign(get_value2());
}

and

// 2. Multiple returns, all of unnamed objects
string g() {
    if (something())
        return get_value1();
    else
        return get_value2();
}

How each of these functions will actually behave in terms of RVO is of course compiler-dependent. Am I right, however, to assume that RVO for both of them is common ?


p.s. (See answers) Function #1 was intended to be the following:

string f() {
    string s;
    if (something())
        return s;
    s.assign(get_value());
    return s;
}
like image 912
n.caillou Avatar asked Jul 11 '16 15:07

n.caillou


1 Answers

For #1, NRVO is guaranteed not to happen, that is, you're guaranteed to get a copy from s to the return value of the function. In this case, you're better off doing

return std::move(s.assign(get_value1()));

Alternatively, if possible, rewrite the function to be NRVO-friendly:

string f() {
    string s;
    if (something())
        s.assign(get_value1());
    else
        s.assign(get_value2());
    return s;
}

Before the compiler even considers NRVO, several Standard requirements have to be met. The one that is not satisfied here is that the expression in the return statement has to be the name of a variable. s.assign(...) is not a name, it is a more complicated expression; you need to have something like return s; for NRVO to be considered.

For #2, assuming the get_value functions return string (or const string), you will most likely have RVO on any modern compiler, and, if all goes well with the ratification of C++17, RVO will be guaranteed in C++17 mode in any conformant compiler (still no guarantees for NRVO).

You can find very good and comprehensive information about (N)RVO (called copy elision in the Standard) on cppreference.com.


I decided to check the current compiler status, so I did some tests on GCC 6.1.0, Clang 3.8.0 and MSVC 2015 Update 3.

For #2, you do get RVO from all three compilers (prvalues in the return statements are easy enough to analyze).

You also get NRVO from all three compilers for a construct like the "NRVO-friendly" one above (for MSVC, you need to have optimizations enabled).

However, for a function like

string f() {
    string s;
    if (something())
        return s;
    s.assign(get_value());
    return s;
}

GCC and Clang do NRVO, but MSVC doesn't; it does however generate moves from s to the return value, which is Standard conforming.

For another example:

string f() {
    string s;
    if (something())
        return get_value1();
    if (something_else())
        return get_value2();
    s.assign(get_value3());
    return s;
}

All three compilers do RVO for the first two returns and a move from s for the third one.

like image 55
bogdan Avatar answered Oct 27 '22 00:10

bogdan