11

让我们考虑这两个函数:

// 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 都很常见?


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

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

1 回答 1

17

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

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

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

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

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

对于#2,假设get_value函数返回string(或const string),您很可能在任何现代编译器上都有 RVO,并且,如果在批准 C++17 时一切顺利,则在任何 C++17 模式下都将保证RVO一致的编译器(仍然不能保证 NRVO)。

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


我决定检查当前的编译器状态,所以我在 GCC 6.1.0、Clang 3.8.0 和 MSVC 2015 Update 3 上做了一些测试。

对于#2,您确实从所有三个编译器中获得了 RVO(return语句中的纯右值很容易分析)。

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

但是,对于像这样的功能

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

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;
}

所有三个编译器都对前两个进行 RVO,对第三个return进行移动s

于 2016-07-11T20:39:45.767 回答