在C++17中编译的constexpr未定义行为从int到作用域枚举的非固定基础类型编译
我想知道以下是否应该或不应该在 C++17 中编译
enum class E
{
A, B
};
constexpr E x = static_cast<E>(2);
这在 Ubuntu 20.04 上使用 GCC 9.3.0 和 Clang 10.0.0 编译。
我的问题是
- 这应该编译吗?
- 如果应该,为什么?
我认为不应该。有很多关于未定义行为 (UB) 和枚举的帖子,但我找不到任何在常量表达式上下文中提出的帖子。此外,大多数帖子使用底层类型,我认为范围枚举没有任何固定的底层类型。因为我没有获得ISO标准的副本,我将把最新的C ++发现有17草案cppreference.com这里在下面的推理。
首先,我们找到将带有 UB 的表达式从常量表达式中丢弃的段落
[expr.const]/2:表达式 e 是核心常量表达式,除非 e 的计算遵循抽象机的规则,将计算以下表达式之一:
带小节
[expr.const]/2.6 : 具有未定义行为的操作,如本国际标准的 [intro] 至 [cpp] 条款 [?注:包括,例如,有符号整数溢出(条款 [expr]),某些指针算术([expr.add]),除以零,或某些移位操作?—?尾注?];
这告诉我们常量表达式可能不包含 UB。
然后,我们找到关于从 int 到 enum 的静态转换的部分
[expr.static.cast]/10 : 整数或枚举类型的值可以显式转换为完整的枚举类型。如果原始值在枚举值 ( [dcl.enum] )的范围内,则该值不变。否则,行为是未定义的。浮点类型的值也可以显式转换为枚举类型。结果值与将原始值转换为枚举的基础类型 ([conv.fpint]) 以及随后的枚举类型相同。
这告诉我们,如果操作数在目标枚举的范围内,则静态转换的结果是明确定义的。该部分参考[decl.enum]/8关于枚举值的范围(太长无法在此处发布,我也无法正确设置格式)。无论如何,它表示非固定基础类型枚举的有效值范围由可以适合最小和最大枚举(以两个补码格式)之间的所有值的最小位集定义。
最后,在示例代码中应用这三个部分,我们可以说可以同时包含 A = 0 和 B = 1 的最小位域的大小为 1。因此,整数 2(需要以两个补码格式表示的两位)在目标枚举 E 的范围之外,并且静态转换具有未定义的行为。由于 constexpr 变量不能具有未定义的行为,因此程序不应编译。
我能得到的最接近答案是在这篇文章中,他们讨论了编译器是否需要在 constexpr 中的 UB 的情况下发出诊断:该帖子的 TLDR 是:是的,但警告可能足以投诉标准。
回答
作用域枚举始终具有固定的基础类型。[dcl.enum]/5 (C++17):
对于作用域枚举类型,
int
如果未明确指定,则底层类型为。在这两种情况下,底层类型都被称为fixed。
所以你E
有固定的基础类型int
. 然后在第 8 段:
对于底层类型固定的枚举,枚举的值就是底层类型的值。
2
在范围内int
,因此根据您从 [expr.static.cast] 引用的文本,演员的行为是明确定义的。