constexpr 静态从 int 转换为具有非固定基础类型的作用域枚举的未定义行为在 C++17 中编译

Undefined behavior of constexpr static cast from int to scoped enum with non-fixed underlying type compiles in C++17

我想知道以下是否应该在 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 进行编译。

我的问题是

  1. 这个应该编译吗?
  2. 如果应该,为什么?

我认为不应该。 有很多关于未定义行为 (UB) 和枚举的 post,但我找不到任何在常量表达式上下文中提出的。此外,大多数 post 使用底层类型,我认为范围枚举没有任何固定的底层类型。由于我无法访问 ISO 标准的副本,因此我将参考最新的 C++17 草案,在下面的推理中找到 cppreference.com here

首先,我们找到了将带有UB的表达式从常量表达式中丢弃的段落

[expr.const]/2: An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

有小节

[expr.const]/2.6: an operation that would have undefined behavior as specified in Clauses [intro] through [cpp] of this International Standard [ Note: including, for example, signed integer overflow (Clause [expr]), certain pointer arithmetic ([expr.add]), division by zero, or certain shift operations  — end note ] ;

这告诉我们常量表达式不能包含 UB。

然后,我们找到有关从 int 到 enum 的静态转换的部分

[expr.static.cast]/10: A value of integral or enumeration type can be explicitly converted to a complete enumeration type. The value is unchanged if the original value is within the range of the enumeration values ([dcl.enum]). Otherwise, the behavior is undefined. A value of floating-point type can also be explicitly converted to an enumeration type. The resulting value is the same as converting the original value to the underlying type of the enumeration ([conv.fpint]), and subsequently to the enumeration type.

这告诉我们,如果操作数在目标枚举的范围内,则静态转换的结果定义明确。该部分参考 [decl.enum]/8 关于枚举值的范围(太长到 post 这里,我也无法正确格式化).无论如何,它表示非固定基础类型枚举的有效值范围由最小位集定义,该位集可以适合最小和最大枚举之间的所有值(以双补码格式)。

最后,将这三个部分应用于示例代码,我们可以说可以同时包含 A = 0 和 B = 1 的最小位域的大小为 1。因此,整数 2(需要两位以二进制补码格式表示)在目标枚举 E 的范围之外,并且静态转换具有未定义的行为。由于 constexpr 变量不能有未定义的行为,程序不应该编译。

我能得到的最接近答案是在 this post 中,他们讨论了在 constexpr 中的 UB 情况下是否需要编译器发出诊断: post 的 TLDR 是:是的,但警告可能足以投诉标准。

作用域枚举始终具有固定的基础类型。 [dcl.enum]/5 (C++17):

For a scoped enumeration type, the underlying type is int if it is not explicitly specified. In both of these cases, the underlying type is said to be fixed.

因此您的 E 具有 int 的固定基础类型。然后在第8段:

For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type.

2int 的范围内,因此根据您从 [expr.static.cast] 引用的文本,强制转换的行为是明确定义的。