C++ 如果您向枚举类 static_cast 无效值会发生什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18195312/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What happens if you static_cast invalid value to enum class?
提问by darth happyface
Consider this C++11 code:
考虑这个 C++11 代码:
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
假设 data[0] 实际上是 100。根据标准设置的颜色是什么?特别是,如果我以后做
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?
作为奖励,标准是否对此做出任何保证,但使用简单的枚举?
采纳答案by dyp
What is color set to according to the standard?
根据标准设置的颜色是什么?
Answering with a quote from the C++11 and C++14 Standards:
引用来自 C++11 和 C++14 标准的引用来回答:
[expr.static.cast]/10
[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).
整数或枚举类型的值可以显式转换为枚举类型。如果原始值在枚举值 (7.2) 的范围内,则该值不变。否则,结果值是未指定的(并且可能不在该范围内)。
Let's look up the range of the enumeration values: [dcl.enum]/7
让我们查找枚举值的范围:[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 (C++11, C++14)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
.
在 CWG 1766 (C++11, C++14) 之前,因此,对于data[0] == 100
,指定结果值 (*),不涉及未定义行为 (UB)。更一般地说,当您从基础类型转换为枚举类型时,没有任何值data[0]
可以导致static_cast
.
After CWG 1766 (C++17)See CWG defect 1766.
The [expr.static.cast]p10 paragraph has been strengthened, so you now caninvoke UB if you cast a value that is outside the representable range of an enum to the enum type. This still doesn't apply to the scenario in the question, since data[0]
is of the underlying type of the enumeration (see above).
在 CWG 1766 (C++17) 之后,请参阅CWG 缺陷 1766。[expr.static.cast]p10 段落已得到加强,因此您现在可以在将超出枚举可表示范围的值转换为枚举类型时调用 UB。这仍然不适用于问题中的场景,因为它data[0]
是枚举的基础类型(见上文)。
Please note that CWG 1766 is considered a defect in the Standard, hence it is accepted for compiler implementers to apply to to their C++11 and C++14 compilation modes.
请注意,CWG 1766 被认为是标准中的一个缺陷,因此编译器实现者可以将其应用于他们的 C++11 和 C++14 编译模式。
(*) 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.
(*)char
要求至少为 8 位宽,但不要求为unsigned
. 可存储的最大值至少127
应符合 C99 标准的附录 E。
Compare to [expr]/4
与 [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.
如果在对表达式求值期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义。
Before CWG 1766, the conversion integral type -> enumeration type can produce an unspecified value. The question is: Can an unspecified value be outside the representable values for its type?I believe the answer is no-- if the answer was yes, there wouldn't be any difference in the guarantees you get for operations on signed types between "this operation produces an unspecified value" and "this operation has undefined behaviour".
在 CWG 1766 之前,转换整数类型 -> 枚举类型可以产生一个未指定的值。问题是:未指定的值是否可以超出其类型的可表示值?我相信答案是否定的——如果答案是肯定的,那么“此操作产生未指定的值”和“此操作具有未定义的行为”之间的有符号类型操作的保证不会有任何区别。
Hence, prior to CWG 1766, even static_cast<Color>(10000)
would notinvoke UB; but after CWG 1766, it doesinvoke UB.
因此,之前CWG 1766,甚至static_cast<Color>(10000)
会不调用UB; 但是在 CWG 1766 之后,它确实调用了 UB。
Now, the switch
statement:
现在,switch
声明:
[stmt.switch]/2
[stmt.switch]/2
The condition shall be of integral type, enumeration type, or class type. [...] Integral promotions are performed.
条件应为整数类型、枚举类型或类类型。[...] 进行整体促销。
[conv.prom]/4
[转换.prom]/4
A prvalue of an unscopedenumeration 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.
基础类型是固定的 (7.2)的无作用域枚举类型的纯右值可以转换为其基础类型的纯右值。此外,如果可以对其底层类型应用整型提升,那么底层类型固定的无作用域枚举类型的纯右值也可以转换为提升的底层类型的纯右值。
Note: The underlying type of a scoped enum w/o enum-baseis 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.
注意:不带enum-base的作用域枚举的基础类型是int
. 对于无作用域枚举,底层类型是实现定义的,但不能大于int
ifint
可以包含所有枚举器的值。
For an unscoped enumeration, this leads us to /1
对于无作用域的枚举,这会将我们引向 /1
A prvalue of an integer type other than
bool
,char16_t
,char32_t
, orwchar_t
whose integer conversion rank (4.13) is less than the rank ofint
can be converted to a prvalue of typeint
ifint
can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of typeunsigned int
.
一个除
bool
,char16_t
,以外的整数类型的纯右值char32_t
,或其wchar_t
整数转换等级 (4.13) 小于 的等级,如果可以表示源类型的所有值,则可以int
转换为类型的纯右值;否则,可以将源纯右值转换为 类型的纯右值。int
int
unsigned int
In the case of an unscopedenumeration, we would be dealing with int
s here. For scopedenumerations (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
.
在无作用域枚举的情况下,我们将在int
这里处理s。对于作用域枚举(enum class
和enum struct
),不适用积分提升。无论如何,积分提升也不会导致 UB,因为存储的值在基础类型的范围内和int
.
[stmt.switch]/5
[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 matchedcase
label. If nocase
constant matches the condition, and if there is adefault
label, control passes to the statement labeled by thedefault
label.
当
switch
语句被执行时,它的条件被评估并与每个 case 常量进行比较。如果 case 常量之一等于条件的值,则控制将传递到匹配case
标签之后的语句。如果没有case
常量与条件匹配,并且有default
标签,则控制传递到由default
标签标记的语句。
The default
label should be hit.
该default
标签应被击中。
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.
注意:人们可以再看一下比较运算符,但它没有明确用于引用的“比较”中。事实上,在我们的例子中,没有任何迹象表明它会为作用域或非作用域枚举引入 UB。
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:
是否enum
有作用域在这里没有任何区别。但是,基础类型是否固定确实有所不同。完整的 [decl.enum]/7 是:
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 eminis the smallest enumerator and emaxis the largest, the values of the enumeration are the values in the range bminto bmax, defined as follows: Let
K
be1
for a two's complement representation and0
for a one's complement or sign-magnitude representation. bmaxis the smallest value greater than or equal to max(|emin| ?K
, |emax|)and equal to 2M? 1, whereM
is a non-negative integer. bminis zero if eminis non-negative and ?(bmax+K
)otherwise.
对于底层类型固定的枚举,枚举的值就是底层类型的值。否则,对于枚举其中Ë分钟是最小的枚举和Ë最大值是最大的,枚举的值是值的范围b分钟至b最大值,定义如下:令
K
是1
对于二的补码表示和0
用于一个补码或符号大小表示。b max是大于或等于max(|e min| ?K
, |e max|)且等于2 的最小值米?1,其中M
是非负整数。如果e min为非负,则b min为零,否则为?(b max+ )。K
Let's have a look at the following enumeration:
我们来看看下面的枚举:
enum ColorUnfixed /* no fixed underlying type */
{
red = 0x1,
yellow = 0x2
}
Note that we cannot define this as a scoped enum, since all scoped enums have fixed underlying types.
请注意,我们不能将其定义为作用域枚举,因为所有作用域枚举都具有固定的基础类型。
Fortunately, ColorUnfixed
'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- 1for 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 bmaxis 3
and bminis 0
.
幸运的是,ColorUnfixed
的最小枚举数是red = 0x1
,所以max(|e min| ? K
, |e max|)等于|e max| 无论如何,这是yellow = 0x2
。大于或等于 的最小值(对于正整数2
等于2 M- 1 )M
是3
( 2 2- 1)。(我认为目的是允许范围以 1 位步长扩展。)因此b maxis3
和bminis 0
。
Therefore, 100
would be outside the range of ColorUnfixed
, and the static_cast
would produce an unspecified value before CWG 1766 and undefined behaviour after CWG 1766.
因此,100
将超出 的范围ColorUnfixed
,并且static_cast
将在 CWG 1766 之前产生未指定的值,在 CWG 1766 之后产生未定义的行为。