This is a very interesting question. The simple answer is that this is literally undefined: the standard doesn't say anything about this case.
To have a better example, consider this enum
:
enum foo : bool { True=true, undefined };
According to the standard:
[dcl.enum]/2: [...] An enumerator-definition without an initializer gives the enumerator the value obtained by increasing the value of the previous enumerator by one.
Therefore, the value of foo::undefined
in our example is 2
(true+1
). Which can not be represented as a bool
.
Is it ill-formed?
No, according to the standard, it is perfectly valid, only not-fixed underlying type have a restriction about not being able to represent all of the enumerator values:
[dcl.enum]/7: For an enumeration whose underlying type is not fixed, [...] If no integral type can represent all the enumerator values, the enumeration is ill-formed.
It says nothing about a fixed underlying type that can not represent all the enumerator values.
What is the value of original question's oops
and undefined
?
It is undefined: the standard doesn't say anything about this case.
Possible values for foo::undefined
:
- Highest possible value (
true
): undefined
and oops
should be underlying type's maximum value.
- Lowest possible value (
false
): the underlying type's minimum value. Note: In signed integers, it would not match the current behavior for Integer overflow (undefined behavior).
- Random value (?): the compiler will choose an value.
The problem with all of these values is that it may result two fields with the same value (e.g. foo::True == foo::undefined
).
The difference between initializer (e.g. undefined=2
) and "implicit" initializer (e.g. True=true, undefined
)
According to the standard:
[dcl.enum]/5: If the underlying type is fixed, the type of each enumerator prior to the closing brace is the underlying type and the constant-expression in the enumerator-definition shall be a converted constant expression of the underlying type.
In other words:
enum bar : bool { undefined=2 };
is equivalent to
enum bar : bool { undefined=static_cast<bool>(2) };
And then bar::undefined
will be true
. In an "implicit" initializer, it would not be the case: this standard paragraph say it about only initializer, and not about "implicit" initializer.
Summary
- With this way, it is possible for an
enum
with a fixed-underlying-type to have unrepresentable values.
- Their value is undefined by the standard.
According to the question and comments, this is not valid in GCC and clang but valid for MSVS-2015 (with a warning).