Оптимизация возвращаемого значения С++, несколько неназванных возвратов

Рассмотрим эти две функции:

// 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

Для # 1 гарантируется, что NRVO не произойдет, то есть вы гарантированно получите копию от s к возвращаемому значению функции. В этом случае вам лучше делать

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

В качестве альтернативы, если это возможно, перепишите функцию как NRVO-friendly:

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 на любом современном компиляторе, и если все будет хорошо с ратификацией С++ 17, RVO будет гарантирован в режиме С++ 17 в любом совместимом компиляторе (все еще нет гарантий для NRVO).

Вы можете найти очень хорошую и исчерпывающую информацию о (N) RVO (называемом копией elision в стандарте) на cppreference.com.


Я решил проверить текущий статус компилятора, поэтому я сделал некоторые тесты в GCC 6.1.0, Clang 3.8.0 и MSVC 2015 Update 3.

Для # 2 вы получаете RVO из всех трех компиляторов (prvalues ​​в операторах return достаточно просты для анализа).

Вы также получаете NRVO из всех трех компиляторов для конструкции, такой как "NRVO-friendly", которая находится выше (для MSVC вам нужно включить оптимизацию).

Однако для такой функции, как

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

GCC и Clang do 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 для третьего.