关于编译器优化
参考: 编译器优化之Copy elision、URVO、NRVO
要点:
1、返回值优化RVO(Return Value Optimization,RVO),即避免返回过程触发复制 / 移动构造函数。根据返回的值是否是匿名对象,可以分为两类:
具名返回值优化 NRVO (Named Return Value Optimization,NRVO)
匿名返回值优化 URVO(Unknown Return Value Optimization,URVO )
2、C++17以前,只有对象的移动构造函数是有效的时,RVO才可以正常使用,而在C++17以后,会强制开启URVO优化(NRVO不会强制开启),此时不再对移动构造函数有依赖(移动构造函数被URVO优化了)。
例如:
class Foo {
public:
Foo() { std::cout<<"default"<<std::endl; }
Foo(const Foo& rhs) { std::cout<<"ctor"<<std::endl; }
// Foo(Foo&& rhs) { std::cout<<"mtor"<<std::endl; }
// Foo(const Foo& rhs) = delete;
Foo(Foo&& rhs) = delete;
};
Foo return_urvo_value() {
return Foo{}; // 若没开启URVO优化,则调用两次移动构造函数
}
Foo return_nrvo_value() {
Foo local_obj;
return local_obj; // 若没开启NRVO优化,也同样调用两次移动构造函数
}
int main() {
auto x = return_urvo_value();
std::cout<<"---------------------------------------"<<std::endl;
auto y = return_nrvo_value();
return 0;
}
在c++11中,当移动构造函数=delete时,不论是否开启RVO,都无法编译通过(由于return_nrvo_value()和return_urvo_value()都依赖移动构造函数).报错:
/home/CppPearlPick/src/mySTL/src/test/test_copy_elision.cpp:15:18: error: use of deleted function ‘Foo::Foo(Foo&&)’
15 | return Foo{}; // 若没开启URVO优化,则调用两次移动构造函数
而当c++17时, 由于URVO强制执行,返回值不再依赖移动构造函数了,因此移动构造函数是否=delete, 都可以编译通过,其实只要移动构造和拷贝构造有任意一个是可以访问的即可。有下面几种情况:
(1)、关闭RVO优化,且移动构造函数=delete
输出为:
对于 auto y = return_nrvo_value(); ,由于URVO强制启用,因此return时匿名对象向y的移动被优化为直接在y空间上对local_obj进行拷贝。
(2)、关闭RVO优化,移动构造函数开启
输出为:
对于 auto y = return_nrvo_value(); ,由于URVO强制启用,因此return时匿名对象向y的移动被优化为直接在y空间上对local_obj进行移动。
(3)、开启RVO优化,且移动构造函数=delete
(4)、开启RVO优化,移动构造函数开启
可以看出,不论是否开启RVO优化,都会强制执行URVO,而NRVO则不会。
总结:C++17以下版本编译器RVO优化是否开启与移动构造函数是否delete,是否关闭RVO优化编译选项有关。
C++17及以上URVO是强制开启的,NRVO能否启用取决于是否开启了RVO优化的编译选项以及移动构造和拷贝构造是否至少有一个可访问。因此C++17及其以上相比于旧版本显然有着更好的编译优化能力。
附:关闭RVO优化,CMakeLists文件内容