三元运算符与直接函数调用场景下的C++对象生存期延长问题
先来看你的代码:
#include <iostream> #include <string> #include <type_traits> struct A{ ~A(){std::cout << "killed '" << a <<"\n";} int a = 3; }; template<typename T> void foo(T&& r){ // Is r still valid here??? std::cout << "Running: " << r.a << "\n"; } int main() { A* a = new A{3}; foo(true ? A{4} : *a); }
你提到的现象很典型:在GCC/Clang环境中输出符合预期,但MSVC 2019 C++14模式下程序崩溃,核心疑问是临时对象的生存期是否被正确延长。
先给结论:你的代码完全符合C++14标准,应该触发生存期延长,MSVC的崩溃是编译器实现的bug。
下面一步步拆解原因:
1. 条件表达式的结果是什么?
你的代码里,true ? A{4} : *a这个条件表达式的两个分支:
A{4}是prvalue(纯右值,即临时对象)*a是lvalue(左值,指向堆上的A实例)
根据C++14标准,当条件表达式的两个操作数分别是prvalue和lvalue时,lvalue分支会被隐式转换为prvalue(也就是拷贝构造一个临时A对象),最终整个表达式的结果是一个prvalue临时对象。因为你的条件是true,实际生效的是A{4}这个prvalue临时对象。
2. 右值引用绑定的生存期延长规则
C++标准明确规定:当prvalue被用来初始化右值引用(或const左值引用)时,临时对象的生存期会被延长到该引用的生存期结束。
在你的代码中,foo是模板函数,传入prvalue时,模板参数T会被推导为A,因此参数r的类型是A&&(右值引用)。这个引用绑定到了条件表达式生成的A{4}临时对象,按照标准,这个临时对象的生存期应该覆盖整个foo函数的执行过程——也就是说,foo里访问r.a是绝对安全的,不会出现悬空引用。
3. 为什么MSVC会崩溃?
这是MSVC 2019在C++14模式下的一个已知实现问题(bug):它没有正确处理条件表达式生成的prvalue绑定到右值引用时的生存期延长逻辑,导致临时对象在foo函数执行前就被销毁了,此时r变成了悬空引用,访问r.a自然会引发崩溃。
你可以尝试切换到MSVC的C17或更高版本标准,这个问题大概率会被修复——因为C17对prvalue的临时物化规则做了调整,间接修复了这类边界场景的生存期处理问题。
内容的提问来源于stack exchange,提问作者Gabriel

