Home > Back-end >  Enumeration conversion and undefined behavior
Enumeration conversion and undefined behavior

Time:03-18

From cpprefernce/static_cast/8:

A value of integer or enumeration type can be converted to any complete enumeration type.

  • If the underlying type is not fixed, the behavior is undefined if the value of expression is out of range (the range is all values possible for the smallest bit field large enough to hold all enumerators of the target enumeration).

I have two questions here:

  • How the underlying type of an enum cannot be fixed. Consider this example:

    enum A : int { i = -1, b = 'c' };

    Does the enum A's underlying type is fixed, regardless of the type of the enumerator values? Does the fixation of the underlying type is determined by either specifying the type or not, regardless of the type of the enumerator values? For example does this enum is fixed enum B { b, c }?

  • How I can determine the range of an enumeration. Consider this example:

    enum N { c = 'A', hex = 0x64 };

    Does the range of enum N is from 65 to 100? Hence the behavior is undefined in the following casts: static_cast<N>(64) // UB? static_cast<N>(101) // UB?

CodePudding user response:

I'm going to write this with a focus on how to read cppreference.com. (This might make it seem more like I am answering one question instead of going over the limit.)

How the underlying type of an enum cannot be fixed.

This is a question about the enumeration type. So if the answer is not on the current page, the next place to look would be the page for enumeration type. Conveniently, on the page you linked to, the phrase "enumeration type" is linked, so you you don't have to search; just click the link.

Once you get to the enumeration type page, you are interested in "fixed". So do a find-in-page (ctrl-f) for "fixed". The first occurrence of "fixed" is highly suggestive of what it means, while the second and third define the term in this context. The definition of an unscoped enumeration type whose underlying type is not fixed looks like form (1) in that section

enum A { i = -1, b = 'c' };

while the definition of an unscoped enumeration type whose underlying type is fixed looks like form (2) in that section

enum A : int { i = -1, b = 'c' };

If you specify the underlying type, then the underlying type is what you specify; it is fixed. If you do not specify the underlying type, then the underlying type is whatever the compiler decides to use; it is not determined in advance (i.e. not fixed).

How I can determine the range of an enumeration

This is given in your quote:

the range is all values possible for the smallest bit field large enough to hold all enumerators of the target enumeration

That's a lot of words, but just take it one piece at a time. Let's use your example:

enum N { c = 'A', hex = 0x64 };
  • The range is all values possible for the smallest bit field large enough to hold all enumerators of the target enumeration.
  • The range is all values possible for the smallest bit field large enough to hold all enumerators of N.
  • The range is all values possible for the smallest bit field large enough to hold 'A' and 0x64.
  • The range is all values possible for the smallest bit field large enough to hold 65 and 100.
  • The range is all values possible for the smallest bit field large enough to hold values representable with 7 bits (unsigned).
  • The range is all values possible for a bit field of length 7.
  • The range is 0 to 127.

While the compiler has some leeway in choosing the underlying type, the underlying type must be able to represent all enumerators. No matter what underlying type is chosen, it will be comprised of bits and be at least as large as the "smallest bit field" from the definition. The range of an enumeration consists of the values that will be representable no matter what underlying type is chosen. Values outside this range might fit in the underlying type, but they might not. Hence, whether or not they can be converted is undefined.

  • Related