Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

As requested by Bathsheba and as a follow up question to "What happens if an enum cannot fit into an integral type?":

Asuming an enum is defined as follows :

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

Is the value of oops defined or isn't it?


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 
like image 394
Simon Kraemer Avatar asked Sep 21 '16 15:09

Simon Kraemer


People also ask

Are enum values signed or unsigned?

An enum type is represented by an underlying integer type. The size of the integer type and whether it is signed is based on the range of values of the enumerated constants. In strict C89 or C99 mode, the compiler allows only enumeration constants with values that will fit in "int" or "unsigned int" (32 bits).

Is enum an integral type?

An enumeration is a user-defined type that consists of a set of named integral constants that are known as enumerators. This article covers the ISO Standard C++ Language enum type and the scoped (or strongly-typed) enum class type which is introduced in C++11.

Is enum always int?

Enumerations are integers, except when they're not - Embedded.com.

Do enums have to be public?

An enum can, just like a class , have attributes and methods. The only difference is that enum constants are public , static and final (unchangeable - cannot be overridden).


1 Answers

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

  1. With this way, it is possible for an enum with a fixed-underlying-type to have unrepresentable values.
  2. 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).

like image 190
Shmuel H. Avatar answered Oct 12 '22 12:10

Shmuel H.