如果static_cast无效值到枚举类会发生什么? [英] What happens if you static_cast invalid value to enum class?

查看:200
本文介绍了如果static_cast无效值到枚举类会发生什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这个C ++ 11代码:

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

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

  switch(color){
// ... red和黄色情况省略
默认值:
//句柄错误
break;
}

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



编辑:



作为奖励,标准是否对此作出任何保证,但使用纯枚举?

解决方案


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


使用标准的报价回答:



[expr.static.cast] / 10


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


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


对于底层类型是固定的枚举,枚举值


在CWG 1766之前
因此,对于 data [0] == 100 ,则指定了结果值(*),且没有涉及未定义行为(UB)。更一般地,当你从底层类型转换为枚举类型时, data [0] 中没有值可以导致 static_cast



After CWG 1766
请参阅 CWG缺陷1766
[expr.static.cast] p10段已加强,因此,如果将枚举类型的可表示范围之外的值转换为枚举类型,则现在调用UB。



(*) char 必须至少8位元, unsigned 。根据C99标准的附录E,可存储的最大值必须至少 127






与[expr] / 4比较


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


可以轻松导致UB,如果值是足够大,例如 static_cast< Color>(10000);






< 开关语句:



[stmt.switch] / 2


条件应为整数类型,枚举类型或类类型。 [...]


[conv.prom] >


底层类型为固定(7.2)的无范围枚举类型的prvalue可以转换为其底层类型。此外,如果可以对其底层类型应用整数提升,则其底层类型是固定的无范围枚举类型的prvalue也可以转换为提升的底层类型的prvalue。


注意:范围枚举w / o 枚举基础的基础类型是 int 。对于无范围的枚举,底层类型是实现定义的,但不能大于 int 如果 int



对于无范围枚举,这会引导我们到/ 1


bool ,char16_t 之外的整数类型的prvalue ,其整数转换排名(4.13)小于的排序的 char32_t wchar_t 如果 int 可以表示所有值,则可以将转换为类型 int 的prval的源类型;否则,源prvalue可以转换为 unsigned int 的prvalue。


无范围枚举的情况下,我们将在此处理 int 。对于作用域枚举(枚举类枚举struct ),不适用整数升级。无论如何,积分提升不会导致UB,因为存储的值在底层类型的范围内并且在 int 的范围内。



[stmt.switch] / 5


语句被执行,其条件被求值并与每个常数进行比较。如果其中一个常量等于条件的值,则控制被传递到匹配的 case 标签之后的语句。如果没有 case 常数匹配条件,并且如果有一个 default 标签,控制传递到默认标签。




注意:我们可以再看一下比较运算符,但它没有明确地用于引用的比较。事实上,在我们的例子中,没有暗示将引入UB作为范围或非范围的枚举。






$



$ b

作为奖励,标准是否对此提供任何保证,但是使用普通枚举? enum 的范围在这里没有任何区别。但是,无论底层类型是否是固定的,它的确有所不同。完整的[decl.enum] / 7是:


对于底层类型是固定的枚举,枚举的值是值的底层类型。否则,对于其中 e min 是最小枚举器并且 e max 枚举是在范围 b min b max 中的值,定义如下:Let K 对于二进制补码表示为 1 ,对于补码为 0 或符号幅度表示。 b max 是大于或等于 max(| e min | - / code>,| e max |)且等于 2 M c> M 是一个非负整数。如果 e min 是非负数,则 b min


,你的枚举最小的枚举是 red = 0x1 ,所以 max(| e min | - code>,| e max |)等于 | e max | code> yellow = 0x2 。最小值大于或等于 2 ,对于正整数<$>等于 2 M c $ c> M
3 2 2 - 1 )。 (我认为其目的是允许范围在1位步长范围内。)接下来是 max 3 bmin 0



c> 100 将在枚举的范围之外,并且 static_cast 将产生未指定的值,这可能导致UB为[expr ] / 4。


Consider this C++11 code:

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

Suppose that data[0] is actually 100. What is color set to according to the standard? In particular, if I later do

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

does the standard guarantee that default will be hit? If not, what is the proper, most efficient, most elegant way to check for an error here?

EDIT:

As a bonus, does the standard make any guarantees as about this but with plain enum?

解决方案

What is color set to according to the standard?

Answering with a quote from the Standard:

[expr.static.cast]/10

A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (7.2). Otherwise, the resulting value is unspecified (and might not be in that range).

Let's look up the range of the enumeration values: [dcl.enum]/7

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

Before CWG 1766 Therefore, for data[0] == 100, the resulting value is specified(*), and no Undefined Behaviour (UB) is involved. More generally, as you cast from the underlying type to the enumeration type, no value in data[0] can lead to UB for the static_cast.

After CWG 1766 See CWG defect 1766. The [expr.static.cast]p10 paragraph has been strengthened, so you now do invoke UB if you cast a value that is outside the representable range of an enum to the enum type.

(*) char is required to be at least 8 bit wide, but isn't required to be unsigned. The maximum value storable is required to be at least 127 per Annex E of the C99 Standard.


Compare to [expr]/4

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.

So this could easily lead to UB if the value was big enough, e.g. static_cast<Color>(10000);.


Now, the switch statement:

[stmt.switch]/2

The condition shall be of integral type, enumeration type, or class type. [...] Integral promotions are performed.

[conv.prom]/4

A prvalue of an unscoped enumeration type whose underlying type is fixed (7.2) can be converted to a prvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, a prvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to a prvalue of the promoted underlying type.

Note: The underlying type of a scoped enum w/o enum-base is int. For unscoped enums the underlying type is implementation-defined, but shall not be larger than int if int can contain the values of all enumerators.

For an unscoped enumeration, this leads us to /1

A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.

In the case of an unscoped enumeration, we would be dealing with ints here. For scoped enumerations (enum class and enum struct), no integral promotion applies. In any way, the integral promotion doesn't lead to UB either, as the stored value is in the range of the underlying type and in the range of int.

[stmt.switch]/5

When the switch statement is executed, its condition is evaluated and compared with each case constant. If one of the case constants is equal to the value of the condition, control is passed to the statement following the matched case label. If no case constant matches the condition, and if there is a default label, control passes to the statement labeled by the default label.

The default label should be hit.

Note: One could take another look at the comparison operator, but it is not explicitly used in the referred "comparison". In fact, there's no hint it would introduce UB for scoped or unscoped enums in our case.


As a bonus, does the standard make any guarantees as about this but with plain enum?

Whether or not the enum is scoped doesn't make any difference here. However, it does make a difference whether or not the underlying type is fixed. The complete [decl.enum]/7 is:

For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, for an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a one's complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin| − K, |emax|) and equal to 2M − 1, where M is a non-negative integer. bmin is zero if emin is non-negative and −(bmax + K) otherwise.

Fortunately, your enum's smallest enumerator is red = 0x1, so max(|emin| − K, |emax|) is equal to |emax| in any case, which is yellow = 0x2. The smallest value greater or equal to 2, which is equal to 2M - 1 for a positive integer M is 3 (22 - 1). (I think the intent is to allow the range to extent in 1-bit-steps.) It follows that bmax is 3 and bmin is 0.

Therefore, 100 would be outside the range of the enum, and the static_cast would produce an unspecified value, which could lead to UB as per [expr]/4.

这篇关于如果static_cast无效值到枚举类会发生什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆