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。
让我们考虑这两个函数:
// 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。