为何GCC中多余花括号阻止RVO?是否违反C++17保证复制消除?
GCC中嵌套花括号阻止NRVO?C++17复制消除的边界解析
先看你提供的测试代码和运行结果:
测试代码
#include <iostream> #define PING() std::cerr << __PRETTY_FUNCTION__ << '\n' struct foo { foo() { PING(); } ~foo() { PING(); } foo(const foo&) { PING(); } }; foo bar() { PING(); foo res; return res; } foo baz() { PING(); { foo res; return res; } } int main() { foo f1 = bar(); foo f2 = baz(); }
GCC 7的运行结果(-std=c++17 -O3)
foo bar() foo::foo() foo baz() foo::foo() foo::foo(const foo&) foo::~foo() foo::~foo() foo::~foo()
Clang 4.0的运行结果
foo bar() foo::foo() foo baz() foo::foo() foo::~foo() foo::~foo()
你疑惑的点在于:为什么baz()里的额外花括号会让GCC放弃返回值优化(RVO),而Clang却能正常优化?这是否属于C++17强制复制消除的范畴?
核心结论:这不属于C++17强制复制消除的场景
首先要明确C++标准里的两种复制消除规则:
- 强制复制消除(C++17及以后):仅针对无名临时对象,比如
return foo();这种直接返回临时对象的情况,标准强制要求编译器消除复制/移动操作,无论是否存在复制/移动构造函数。 - 命名返回值优化(NRVO):针对返回命名局部变量的情况(比如你的代码里的
res),这是标准允许但不强制的优化——编译器可以选择是否进行优化,哪怕是C++17及以后的版本,也没有要求必须做NRVO。
为什么GCC和Clang行为不同?
- 对于
bar():返回的res是函数直接作用域下的局部变量,编译器很容易识别出这个变量的唯一用途就是作为返回值,所以GCC和Clang都能顺利应用NRVO,消除复制。 - 对于
baz():res被包裹在内层花括号的作用域中。GCC 7的优化器可能认为这个内层作用域限制了res的生命周期,无法直接将res的内存与函数返回值的目标内存复用;而Clang的优化器则更聪明,它能识别出return res;会直接结束内层作用域和外层函数,因此可以安全地复用res的内存,完成NRVO。
这种差异完全符合标准——因为NRVO是可选优化,不同编译器(甚至同一编译器的不同版本)的优化策略可以不同。
解决建议
如果你希望GCC也能在这种嵌套作用域的场景下做NRVO,可以尝试升级到更新版本的GCC(比如GCC 10及以后),新版本的优化器已经改进了这种场景的识别能力,大概率会支持baz()的NRVO。
内容的提问来源于stack exchange,提问作者akim
相关产品推荐
相关产品推荐

