GCC/Clang开启优化时minmax结合结构化绑定输出异常是bug吗?
问题描述
使用-std=c++20编译标志构建如下程序:
#include <iostream> using namespace std; int main() { auto [n, m] = minmax(3, 4); cout << n << " " << m << endl; }
实际运行表现和编译器、优化等级强相关:
- 不开启
-Ox系列优化时,程序输出预期结果3 4 - 多个GCC版本下开启
-O1/-O2/-O3优化,程序统一输出0 0 - Clang 13运行结果正常,Clang 10、11开启
-O2及以上优化时输出0 4198864这类随机值 - Icc编译器所有优化等级下运行都正常
根本原因
这个现象是悬垂引用触发的未定义行为,本质是对std::minmax的返回规则不熟悉,叠加旧版本编译器标准库实现的缺陷共同导致:
- 直接给
std::minmax传入3、4这类纯右值临时量时,对应重载的返回值是std::pair<const int&, const int&>——返回的不是两个整数值,而是绑定到临时量3、4的const引用。 - C++的临时对象生命周期规则明确:临时对象会在包含它的完整表达式结束时销毁。这里的完整表达式就是
auto [n, m] = minmax(3, 4);整行,等执行到下一行cout输出时,3和4两个临时量已经被销毁。 - 结构化绑定只会延长
std::pair本身的生命周期,不会延长pair内部引用所指向对象的生命周期,所以n和m实际是两个悬垂引用,访问悬垂引用属于标准明确规定的未定义行为,输出什么值完全看编译器怎么处理栈空间和寄存器:- 无优化时,栈空间不会被主动清理,被销毁的临时值还残留在原内存位置,所以能正常输出
3 4 - 开优化后,编译器会复用栈空间、清理无用临时值,就会读到残留的垃圾值:GCC的优化逻辑会把对应栈位置初始化为0,旧版Clang直接读未初始化的栈内存出随机值,新版Clang和Icc要么对这个场景做了特殊兼容,要么优化逻辑刚好没覆写对应内存,所以表现正常。
- 无优化时,栈空间不会被主动清理,被销毁的临时值还残留在原内存位置,所以能正常输出
修复方法很简单:要么先把3、4存成左值变量再传入minmax,要么显式指定minmax返回值类型为值语义,写成
auto [n, m] = minmax<int>(3, 4);,此时返回std::pair<int, int>值对象,不存在引用悬垂问题。
内容的提问来源于stack exchange,提问作者Paul Jurczak
相关产品推荐
相关产品推荐

