Ответ 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
для третьего.