C ++返回值优化,多个未命名的返回

时间:2016-07-11 15:56:31

标签: c++ rvo

让我们考虑这两个功能:

// 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());
}

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

这些函数中每个函数在RVO方面的实际行为当然是编译器相关的。但是,我是否认为两者的RVO都很常见?

p.s。(见答案)功能#1旨在如下:

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

1 个答案:

答案 0 :(得分:14)

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.