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

时间:2013-08-12 19:38:17

标签: c++ c++11

考虑这个C ++ 11代码:

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;
}

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

编辑:

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

1 个答案:

答案 0 :(得分:114)

  

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

回答C ++ 11和C ++ 14标准中的引用:

[expr.static.cast] / 10

  

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

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

  

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

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

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

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

(*)char必须至少为8位宽,但不要求unsigned。根据C99标准的附件E,可存储的最大值必须至少为127


与[expr] / 4

比较
  

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

在CWG 1766之前,转换积分类型 - &gt;枚举类型可以生成未指定的值。问题是:未指定的值是否可以超出其类型的可表示值?我认为答案是 - 如果答案是,对于#34之间签名类型的操作所获得的保证不会有任何差别;此操作会产生未指定的值&#34;和&#34;此操作具有未定义的行为&#34;。

因此,在CWG 1766之前,即使static_cast<Color>(10000) 也不会调用UB;但是在CWG 1766之后,确实调用UB。


现在,switch声明:

[stmt.switch] / 2

  

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

[conv.prom / 4

  

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

注意:带有 enum-base 的作用域枚举的基础类型是int。对于未作用域的枚举,基础类型是实现定义的,但如果int可以包含所有枚举数的值,则不应大于int

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

  

boolchar16_tchar32_twchar_t以外的整数类型的prvalue,其整数转换等级(4.13)小于{{的等级如果int可以表示源类型的所有值,则可以将1}}转换为int类型的prvalue;否则,源prvalue可以转换为int类型的prvalue。

如果是 unscoped 枚举,我们将在此处理unsigned int s。对于范围枚举(intenum class),不适用整数促销。无论如何,积分促销也不会导致UB,因为存储的值在基础类型的范围内且在enum struct的范围内。

[stmt.switch] / 5

  

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

应该点击default标签。

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


  

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

default是否具有作用范围并不会产生任何影响。但是,无论底层类型是否固定,它确实有所不同。完整的[decl.enum] / 7是:

  

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

让我们看一下以下枚举:

M

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

幸运的是,K的最小枚举数为enum ColorUnfixed /* no fixed underlying type */ { red = 0x1, yellow = 0x2 } ,因此 max(| e min | - ColorUnfixed,| e在任何情况下, max |)等于 | e max | ,即red = 0x1。对于正整数K,大于或等于yellow = 0x2的最小值等于 2 M - 1 2 2 2 - 1 )。 (我认为目的是允许范围以1位步进扩展。)接下来 b max M bmin 3

因此,3将超出0的范围,100将在CWG 1766之前生成未指定的值,并在CWG 1766之后生成未定义的行为。