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

C++ return value optimization, multiple unnamed returns

让我们考虑这两个函数:

// 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,NRVO 保证 不会 发生,也就是说,您保证从 s 到 return 获得副本功能的价值。在这种情况下,你最好做

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 函数 return string(或 const string),您很可能在任何现代编译器上都有 RVO,并且,如果随着 C++17 的批准一切顺利,RVO 将在任何符合标准的编译器中以 C++17 模式保证(仍然不能保证 NRVO)。

您可以在 cppreference.com.

上找到关于 (N)RVO(在标准中称为 copy elision)的非常好的和全面的信息

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

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

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

但是,对于像

这样的函数
string f() {
    string s;
    if (something())
        return s;
    s.assign(get_value());
    return s;
}

GCC 和 Clang 支持 NRVO,但 MSVC 不支持;但是它确实会生成从 s 到 return 值的移动,这是符合标准的。

再举个例子:

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

所有三个编译器都对前两个 return 执行 RVO,对第三个 s 执行 RVO。