What happens if an enumeration cannot fit into an unsigned integral type?

As requested by Bathsheba and as a follow-up question, “What happens if an enumeration cannot fit into an integral type?” :

The transfer unit is defined as follows:

enum foo : unsigned int { bar = UINT_MAX, oops }; 

Is oops defined or not?




MSVS2015 compilation:

 warning C4340: 'oops': value wrapped from positive to negative value warning C4309: 'initializing': truncation of constant value warning C4369: 'oops': enumerator value '4294967296' cannot be represented as 'unsigned int', value is '0' 

MSVS2015 Output:

 bar = 4294967295 oops= 0 



gcc 4.9.2 compilation:

 9 : note: in expansion of macro 'UINT_MAX' bar = UINT_MAX, ^ 10 : error: enumerator value 4294967296l is outside the range of underlying type 'unsigned int' oops ^ Compilation failed 

gcc 4.9.2 output

 //compilation failed 
+44
c ++ enums language-lawyer types
Sep 21 '16 at 15:44
source share
1 answer

This is a very interesting question. The simple answer is that it is literally undefined: the standard says nothing about this case.

To have a better example, consider this enum :

  enum foo : bool { True=true, undefined }; 

In accordance with the standard:

[dcl.enum] / 2: [...] Defining an enumerator 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 cannot be represented as a bool .

Is it badly formed?

No , according to the standard, this is absolutely true, only the non-fixed base type has a restriction on the impossibility of representing all counter values:

[dcl.enum] / 7: for an enumeration whose main type is not fixed, [...] If the integral type cannot represent all counter values, the enumeration is poorly formed.

It does not say anything about a fixed base type that cannot represent all counter values.

What is the meaning of the original oops and undefined question?

This is undefined : the standard says nothing about this case.

Possible values ​​for foo::undefined :

  • Maximum possible value ( true ): undefined and oops should be the maximum value of the base type.
  • Lowest possible value ( false ): The minimum value of the base type. Note. In signed integers, this will not match the current behavior for Integer overflow (undefined).
  • Random value (?): The compiler will select a value.

The problem with all of these values ​​is that it can lead to two fields with the same value (for example, foo::True == foo::undefined ).

The difference between an initializer (for example, undefined=2 ) and an "implicit" initializer (for example, True=true, undefined )

In accordance with the standard:

[dcl.enum] / 5: If the base type is fixed, the type of each enumerator before the closing bracket is the base type, and the constant expression in the enumeration definition must be a constant type transformed by the base type.

In other words:

  enum bar : bool { undefined=2 }; 

equivalently

  enum bar : bool { undefined=static_cast<bool>(2) }; 

And then bar::undefined will be true . This is not so in the “implicit” initializer: this standard paragraph only speaks about this initializer, and not about the “implicit” initializer.

Summary

  • Thus, it is possible that enum with a fixed base type have underrepresented values.
  • Their value is undefined by standard.

According to the question and comments, this is not valid in GCC and clang, but is relevant for MSVS-2015 (with a warning).

+18
Sep 21 '16 at 19:13
source share



All Articles