如果 static _ cast 将无效值转换为枚举类会发生什么?

考虑下面的 C + + 代码:

enum class Color : char { red = 0x1, yellow = 0x2 }
// ...
char *data = ReadFile();
Color color = static_cast<Color>(data[0]);

假设数据[0]实际上是100。根据标准,颜色设置为什么? 特别是,如果我以后这样做

switch (color) {
// ... red and yellow cases omitted
default:
// handle error
break;
}

是否标准保证违约将被触发?如果没有,那么在这里检查错误的最合适、最有效、最优雅的方法是什么?对于这个标准,除了使用普通枚举之外,是否做出了任何保证?

58073 次浏览

根据标准设置的颜色是什么?

引用 C + + 11和 C + + 14标准:

[ expr.static.cast ]/10

可以将整数类型或枚举类型的值显式转换为枚举类型。如果原始值在枚举值(7.2)的范围内,则该值不变。否则,结果值是未指定的(并且可能不在该范围内)。

让我们查看 枚举值的范围: [ dcl.enum ]/7

对于基础类型固定的枚举,枚举的值是基础类型的值。

CWG 1766之前(C + + 11,C + + 14) 因此,对于 data[0] == 100,将指定结果值(*) ,并且不涉及 未定义行为(UB)。更一般地说,当您从基础类型转换为枚举类型时,data[0]中的任何值都不会导致 static_cast的 UB。

CWG 1766之后(C + + 17) 参见 CWG 缺陷1766。 [ expr.static.cast ] p10段落已经得到了加强,因此,如果您将超出枚举可表示范围的值转换为枚举类型,那么现在 可以将调用 UB。这仍然不适用于问题中的场景,因为 data[0]是枚举的基础类型(见上文)。

请注意,CWG 1766被认为是标准中的一个缺陷,因此编译器实现者可以将它应用于他们的 C + + 11和 C + + 14编译模式。

(*) char需要至少8位宽,但不需要是 unsigned。每个 C99标准的附件 E 要求可储存的最大值至少为 127


与[ expr ]/4相比

如果在计算表达式期间,结果没有在数学上定义,或者没有在其类型的可表示值的范围内,则该行为是未定义的。

在 CWG 1766之前,转换整数类型-> 枚举类型可以生成 未指定的值。问题是: 未指定的值是否可以在其类型的可表示值之外?我相信答案是 没有——如果答案是 是的,那么对于有符号类型的操作,在“这个操作产生一个未指定的值”和“这个操作具有未定义的行为”之间得到的保证是没有任何区别的。

因此,在 CWG 1766之前,甚至 static_cast<Color>(10000)也会 没有调用 UB; 但是在 CWG 1766之后,是的调用 UB。


现在,switch声明:

[ stmt.switch ]/2

条件应为整数类型、枚举类型或类类型

[舞会]/4

其基础类型是固定的(7.2)的 不用瞄准枚举类型的 prvalue 可以转换为其基础类型的 prvalue。此外,如果可以对其基础类型应用整数提升,则其基础类型是固定的非范围枚举类型的 prvalue 也可以转换为提升的基础类型的 prvalue。

注意: 作用域枚举 w/o Enum-base的底层类型是 int。对于非范围枚举,基础类型是实现定义的,但是如果 int可以包含所有枚举器的值,则基础类型不应大于 int

对于 无作用域的枚举来说,这是1

boolchar16_tchar32_twchar_t外,其整数转换秩(4.13)小于 int秩的整数类型的预估值,如果 int可以表示源类型的所有值,则可以转换为 int类型的预估值; 否则,可以将源预估值转换为 unsigned int类型的预估值。

不用瞄准枚举的情况下,我们将在这里处理 int。对于 瞄准枚举(enum classenum struct) ,不适用整数提升。无论如何,整合提升也不会导致 UB,因为存储值在底层类型的范围内,在 int的范围内。

[ stmt.switch ]/5

执行 switch语句时,将计算其条件并与每个大小写常数进行比较。如果其中一个 case 常量等于条件的值,则控制权将传递给匹配的 case标签后面的语句。如果没有与条件匹配的 case常量,并且有 default标签,则控制权将传递给由 default标签标记的语句。

应该点击 default标签。

注意: 我们可以再看一下比较运算符,但是在引用的“比较”中没有显式使用它。事实上,在我们的例子中,没有任何迹象表明它会为有作用域或无作用域的枚举引入 UB。


作为额外的好处,标准是否对此做出任何保证,但是使用普通枚举?

enum是否有作用域在这里没有任何区别。但是,无论基础类型是否固定,都会产生差异。完整的[ dec.enum ]/7是:

对于基础类型固定的枚举,枚举的值是基础类型的值。否则,对于一个枚举,其中 E < sub > min 是最小的枚举数,而 E < sub > max 是最大的,枚举的值是范围从 B < sub > min B < sub > max 的值,定义如下: 设 K1,表示2的补数,0是表示1的补数或符号大小的表示。B < sub > max 是大于或等于 11的最小值,等于 12,其中 M是非负整数。如果 E < sub > min 为非负值,则 B < sub > min 为零,否则 15为零。

让我们看看下面的枚举:

enum ColorUnfixed /* no fixed underlying type */
{
red = 0x1,
yellow = 0x2
}

注意,我们不能将其定义为作用域枚举,因为所有作用域枚举都具有固定的基础类型。

幸运的是,ColorUnfixed的最小枚举数是 red = 0x1,因此 最大(| e < sub > min |-K,| e < sub > max |)在任何情况下都等于 red = 0x10,即 yellow = 0x2。大于或等于 2的最小值,对于正整数 M等于 red = 0x11的值是 3(red = 0x12)。(我认为这样做的目的是允许1位步长的范围。)因此,red = 0x13是 3red = 0x14是 0

因此,100将在 ColorUnfixed的范围之外,而 static_cast将在 CWG 1766之前产生未指定的值,在 CWG 1766之后产生未指定的行为。