C++常量求值器是否必须拒绝存在未定义行为的union示例代码?
问题描述
据我所知,在常量求值过程中出现未定义行为应当触发编译错误。
但如果对C++20标准class.union#6.3章节给出的未定义行为示例做少量修改以触发常量求值,代码如下:
struct X { const int a; int b; }; union Y { X x; int k; }; constexpr bool g() { Y y = { { 1, 2 } }; // OK, y.x是活跃的union成员 ([class.mem]) int n = y.x.a; y.k = 4; // OK: 结束y.x的生命周期,y.k成为union的活跃成员 y.x.b = n; // 未定义行为:y.x.b在其生命周期外被修改 // 因为X的默认构造函数被删除,S(y.x.b)为空 // 因此union成员y.x的生命周期不会隐式启动 return y.x.b > 0; } int main() { static_assert( g() ); }
上述代码被所有编译器接受且无任何警告输出,请问这种情况是所有编译器实现错误,还是该示例不存在未定义行为,或是标准不要求编译器对此给出诊断?
回答
这一现象属于标准未要求强制诊断的未定义行为场景,既不意味着代码不存在未定义行为,也不构成编译器的标准违反,具体说明如下:
- 首先可以明确代码确实存在未定义行为,完全符合C++20 [class.union#6.3]的规定场景。
X类型没有用户声明的默认构造函数,在y.k被赋值为活跃成员后,y.x的生命周期已经终止,后续直接修改y.x.b时,无法隐式启动y.x的生命周期,操作性质明确属于未定义行为。 - C++标准仅强制要求编译器对常量求值过程中明确列入禁止范围的操作给出诊断,其余未定义行为均属于“无需诊断的未定义行为”范畴,编译器没有必须检测和报错的义务。你遇到的这个union生命周期相关的UB就属于后者,标准没有强制要求编译器在常量求值时做检查。
- 目前GCC、Clang、MSVC等主流编译器均未实现对这个特定场景的常量求值检查,因此会直接接受代码。这类检查属于编译器的质量提升项,而非合规强制要求,因此不属于编译器实现错误。
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

