Home > Software design >  Is enum { a } e = 1; valid?
Is enum { a } e = 1; valid?

Time:02-05

A simple question: is enum { a } e = 1; valid?

In other words: is it well-defined behavior to assign a value, which isn't present in the set of values of enumeration constants?

Demo:

$ gcc t0.c -std=c11 -pedantic -Wall -Wextra -c
<nothing>

$ clang t0.c -std=c11 -pedantic -Wall -Wextra -c
<nothing>

$ icc t0.c -std=c11 -pedantic -Wall -Wextra
t0.c(1): warning #188: enumerated type mixed with another type
# note: the same warning for enum { a } e = 0;

$ cl t0.c /std:c11 /Za /c
<nothing>

CodePudding user response:

From the C18 standard in 6.7.2.2:

Each enumerated type shall be compatible with char , a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration.

So yes enum { a } e = 1; is valid. e is a 'integer' type so it can take the value 1. The fact that 1 is not present as an enumeration value is no issue. The enumeration members only give handy identifiers for some of possible values.

CodePudding user response:

I can find no reference in this Draft C11 Standard (in terms of "constraints") that prohibits an assignment to a enum type such as yours (and the C18 Standard quoted in this other answer uses essentially identical wording).

However, that C11 Draft does provide this, in Annexe I – Common Warnings:

1     An implementation may generate warnings in many situations, none of which are specified as part of this International Standard. The following are a few of the more common situations.

2

   —    A value is given to an object of an enumerated type other than by assignment of an enumeration constant that is a member of that type, or an enumeration object that has the same type, or the value of a function that returns the same enumerated type (6.7.2.2).

But that suggested warning could equally well apply to an assignment like enum { a } e = 0;, where the value of the RHS corresponds to a valid enumeration constant but it is actually neither an enumeration constant of the type nor an object of that enumerated type.

CodePudding user response:

Valid in C, implementation-defined and possible unspecified/undefined behaviour in C 14/C 17

Whilst this is valid in C as per e.g. the 202x working draft:

6.7.2.2/4 Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration

due to the stronger "compatible type" requirement, it may be interesting to point out that the same does not hold for C , where "C style" unscoped enums with no fixed underlying type have subtle differences from C: this being a key one.

If we consider the similar example syntactically valid in C :

// C   does not allow implicit conversion _from_ an integer type
// _to_ an unscoped enum with no explicit underlying type, so we'll
// need to resort to explicit conversions for a similar example
enum E { a } e = static_cast<E>(2);  // #1

// or ...
enum E { a } e = E(2);

As per [dcl.enum]/7,

For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, the values of the enumeration are the values representable by a hypothetical integer type with minimal width M such that all enumerators can be represented. The width of the smallest bit-field large enough to hold all the values of the enumeration type is M. It is possible to define an enumeration that has values not defined by any of its enumerators. If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a single enumerator with value 0.

and [expr.static.cast]/10:

A value of integral or enumeration type can be explicitly converted to a complete enumeration type. If the enumeration type has a fixed underlying type, the value is first converted to that type by integral conversion, if necessary, and then to the enumeration type. If the enumeration type does not have a fixed underlying type, the value is unchanged if the original value is within the range of the enumeration values ([dcl.enum]), and otherwise, the behavior is undefined. A value of floating-point type can also be explicitly converted to an enumeration type. The resulting value is the same as converting the original value to the underlying type of the enumeration ([conv.fpint]), and subsequently to the enumeration type.

#2 above may (depending on the implementation-defined underlying type of the E) lead to undefined behavior (used to be unspecified behaviour in until C 17 via CWG 1766), conceptually possible cause signed integer overflow (which is UB).

  •  Tags:  
  • Related