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

n.c*_*lou 10 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());
}
Run Code Online (Sandbox Code Playgroud)

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

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


ps(见答案)功能#1旨在如下:

string f() {
    string s;
    if (something())
        return s;
    s.assign(get_value());
    return s;
}
Run Code Online (Sandbox Code Playgroud)

bog*_*dan 15

对于#1,保证NRVO 不会发生,也就是说,您可以保证从s函数的返回值获得副本.在这种情况下,你最好这样做

return std::move(s.assign(get_value1()));
Run Code Online (Sandbox Code Playgroud)

或者,如果可能,将该函数重写为NRVO友好的:

string f() {
    string s;
    if (something())
        s.assign(get_value1());
    else
        s.assign(get_value2());
    return s;
}
Run Code Online (Sandbox Code Playgroud)

在编译器甚至考虑NRVO之前,必须满足几个标准要求.这里不满意的是return语句中的表达式必须是变量的名称.s.assign(...)不是一个名字,它是一个更复杂的表达; 你需要return s;考虑像NRVO 这样的东西.

对于#2,假设get_value函数返回string(或const string),你很可能在任何现代编译器上都有RVO,并且,如果一切顺利,批准C++ 17,RVO将在C++ 17模式中得到保证.符合编译器(仍不保证NRVO).

您可以在cppreference.com上找到关于(N)RVO(在标准中称为复制省略)的非常好的和全面的信息.


我决定检查当前的编译器状态,因此我对GCC 6.1.0,Clang 3.8.0和MSVC 2015 Update 3进行了一些测试.

对于#2,您可以从所有三个编译器获得RVO(return语句中的prvalues 很容易分析).

您还可以从所有三个编译器获得NRVO以获得类似上面的"NRVO友好"构造(对于MSVC,您需要启用优化).

但是,对于像这样的功能

string f() {
    string s;
    if (something())
        return s;
    s.assign(get_value());
    return s;
}
Run Code Online (Sandbox Code Playgroud)

GCC和Clang做NRVO,但MSVC没有; 然而它会产生从s返回值的移动,这是标准符合.

再举一个例子:

string f() {
    string s;
    if (something())
        return get_value1();
    if (something_else())
        return get_value2();
    s.assign(get_value3());
    return s;
}
Run Code Online (Sandbox Code Playgroud)

所有三个编译器都会在前两个版本中执行RVO,return而从s第三个编译器执行此操作.