# c++ - values - What happens if you static_cast invalid value to enum class?

## what is static_cast in c++ (1)

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 C++11 and C++14 Standards:

[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 (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`

.

**After CWG 1766 (C++17)**
See CWG defect 1766.
The [expr.static.cast]p10 paragraph has been strengthened, so you now **can** invoke 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).

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.

_{(*) 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.

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".

Hence, prior to CWG 1766, even `static_cast<Color>(10000)`

would **not** invoke UB; but after CWG 1766, it **does** invoke UB.

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

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.

_{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 `int`

s 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

eis the smallest enumerator and_{min}eis the largest, the values of the enumeration are the values in the range_{max}bto_{min}b, defined as follows: Let_{max}`K`

be`1`

for a two's complement representation and`0`

for a one's complement or sign-magnitude representation.bis the smallest value greater than or equal to_{max}max(|eand equal to_{min}| −`K`

, |e_{max}|)2, where^{M}− 1`M`

is a non-negative integer.bis zero if_{min}eis non-negative and_{min}−(botherwise._{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(|e _{min}| − K, |e_{max}|)* is equal to

*|e*in any case, which is

_{max}|`yellow = 0x2`

. The smallest value greater or equal to `2`

, which is equal to *2*for a positive integer

^{M}- 1`M`

is `3`

(*2*). (I think the intent is to allow the range to extent in 1-bit-steps.) It follows that

^{2}- 1*b*is

_{max}`3`

and *bmin*is

`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.