让我们考虑这两个功能:
// 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;
}
答案 0 :(得分:14)
For #1, NRVO is guaranteed not to happen, that is, you're guaranteed to get a copy from s
to the return value of the function. In this case, you're better off doing
return std::move(s.assign(get_value1()));
Alternatively, if possible, rewrite the function to be NRVO-friendly:
string f() {
string s;
if (something())
s.assign(get_value1());
else
s.assign(get_value2());
return s;
}
Before the compiler even considers NRVO, several Standard requirements have to be met. The one that is not satisfied here is that the expression in the return
statement has to be the name of a variable. s.assign(...)
is not a name, it is a more complicated expression; you need to have something like return s;
for NRVO to be considered.
For #2, assuming the get_value
functions return string
(or const string
), you will most likely have RVO on any modern compiler, and, if all goes well with the ratification of C++17, RVO will be guaranteed in C++17 mode in any conformant compiler (still no guarantees for NRVO).
You can find very good and comprehensive information about (N)RVO (called copy elision in the Standard) on cppreference.com.
I decided to check the current compiler status, so I did some tests on GCC 6.1.0, Clang 3.8.0 and MSVC 2015 Update 3.
For #2, you do get RVO from all three compilers (prvalues in the return
statements are easy enough to analyze).
You also get NRVO from all three compilers for a construct like the "NRVO-friendly" one above (for MSVC, you need to have optimizations enabled).
However, for a function like
string f() {
string s;
if (something())
return s;
s.assign(get_value());
return s;
}
GCC and Clang do NRVO, but MSVC doesn't; it does however generate moves from s
to the return value, which is Standard conforming.
For another example:
string f() {
string s;
if (something())
return get_value1();
if (something_else())
return get_value2();
s.assign(get_value3());
return s;
}
All three compilers do RVO for the first two return
s and a move from s
for the third one.