如果你为枚举类static_cast无效值会发生什么?

dar*_*ace 136 c++ c++11

考虑一下这个C++ 11代码:

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

假设数据[0]实际上是100.根据标准设置的颜色是什么?特别是如果我以后做的话

switch (color) {
    // ... red and yellow cases omitted
    default:
        // handle error
        break;
}
Run Code Online (Sandbox Code Playgroud)

标准是否保证会触发默认值?如果没有,在这里检查错误的正确,最有效,最优雅的方法是什么?

编辑:

作为奖励,该标准是否对此有任何保证,但是用简单的枚举?

dyp*_*dyp 121

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

回答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]可以导致UB static_cast.

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

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

(*)char必须至少为8位宽,但不是必须的unsigned.可存储的最大值至少127应符合C99标准的附录E.


与[expr]/4比较

如果在评估表达式期间,结果未在数学上定义或未在其类型的可表示值范围内,则行为未定义.

在CWG 1766之前,转换整数类型 - >枚举类型可以产生未指定的值.问题是:未指定的值是否可以超出其类型的可表示值?我相信答案是否定的 - 如果答案是肯定的,那么在"此操作产生未指定的值"和"此操作具有未定义的行为"之间对签名类型的操作所获得的保证不会有任何差异.

因此,之前CWG 1766,甚至static_cast<Color>(10000)不会调用UB; 但是在CWG 1766之后,它确实调用了UB.


现在,switch声明:

[stmt.switch]/2

条件应为整数类型,枚举类型或类类型.[...] 执行整体促销.

[conv.prom/4

其基础类型为固定(7.2)的未作用域枚举类型的prvalue可以转换为其基础类型的prvalue.此外,如果可以对其基础类型应用整数提升,则其基础类型固定的无范围枚举类型的prvalue也可以转换为提升的基础类型的prvalue.

注意:带有enum-base的作用域枚举的基础类型是int.对于未作用域的枚举,底层类型是实现定义的,但不能大于intif是否int可以包含所有枚举器的值.

对于无范围的枚举,这会将我们引向/ 1

以外的整数类型的prvalue bool,char16_t,char32_t,或wchar_t,其整数转换秩(4.13)小于的秩int可以被转换成类型的prvalue int如果int可以表示源类型的所有值; 否则,源prvalue可以转换为类型的prvalue unsigned int.

在没有范围的枚举的情况下,我们将在int这里处理s.对于作用域枚举(enum classenum struct),不适用整数提升.无论如何,积分促销也不会导致UB,因为存储的值在基础类型的范围内并且在范围内int.

[stmt.switch]/5

switch被执行的语句,其条件评价,并与每一种情况下恒定比较.如果其中一个case常量等于条件的值,则将控制权传递给匹配case标签后面的语句.如果没有case常量匹配条件,并且如果有default标签,则控制传递到标签标记的语句default.

default标签应被击中.

注意:可以再看一下比较运算符,但它没有在引用的"比较"中明确使用.事实上,在我们的案例中,没有任何暗示它会为范围或未范围的枚举引入UB.


作为奖励,该标准是否对此有任何保证,但是用简单的枚举?

是否enum作用范围在这里没有任何区别.但是,无论底层类型是否固定,它确实有所不同.完整的[decl.enum]/7是:

对于其基础类型是固定的枚举,枚举的值是基础类型的值.否则,对于枚举,其中e min是最小的枚举数且e max是最大的,枚举的值是b minb max范围内的值,定义如下:K设为12的补码表示和0a一个补码或符号幅度表示.b max是大于或等于max(| e min | - K,| e max |)并且等于2 M -1的最小值,其中M是非负整数.如果e min是非负的,则b min为零,否则为- (b max + ).K

我们来看看下面的枚举:

enum ColorUnfixed /* no fixed underlying type */
{
    red = 0x1,
    yellow = 0x2
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们无法将其定义为作用域枚举,因为所有作用域枚举都具有固定的基础类型.

幸运的是,ColorUnfixed最小的枚举器是red = 0x1,所以max(| e min | - K,| e max |)等于| e max | 无论如何,这是yellow = 0x2.大于或等于的最小值2,对于正整数,等于2 M -1M3(2 2 -1).(我认为目的是允许范围以1位步进扩展.)接着是b max3bmin0.

因此,100将在CWG 1766之前产生未指定的值ColorUnfixed,并且static_cast在CWG 1766之后产生未定义的行为.

  • 基础类型是固定的,因此枚举值的范围(§7.2[dcl.enum] p7)是"基础类型的值".100肯定是`char`的值,所以"如果原始值在枚举值(7.2)的范围内,则值不变." 适用. (3认同)
  • 我不得不寻找"UB"的意思.('未定义的行为')问题没有提到未定义行为的可能性; 所以我没想到你可能会谈论这个. (2认同)
  • @karadoc我在第一次出现时添加了一个链接. (2认同)