Do all C++ enums have at least two legal values?

Viewed 431

While working with some custom comparators, I encountered the need for a type that only had a single possible value. There are types like std::nullptr_t and empty structs where this is the case.

Then I considered the possibility of using an enum. I might declare an enum with a single value, something like

enum E
{
  only_value // BUT IS IT??
}; 

But it seems that the standard says that all of the underlying type's values, which fit into the "smallest bitfield" that can contain the declared values, are valid.

From cppreference.com:

(The source value, as converted to the enumeration's underlying type if floating-point, is in range if it would fit in the smallest bit field large enough to hold all enumerators of the target enumeration.)

If you declare an enum with only a single enumerator, the smallest it can be is one bit. Following that logic, the unnamed enumerator with the bit's other value should be legal. If an enum is based on a signed integer, then -1 and 0 are always legal. On an unsigned integer, 0 and 1 are always legal.

Is there something else in the standard that makes the unnamed bit value illegal or UB?

1 Answers

It appears that enum E { value = 0; } only has one value, that of 0. The standard has this to say:

[dcl.enum]/8 ... Otherwise [if the underlying type is not fixed - IT], for an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a ones' complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin| − K, |emaxn|) and equal to 2M − 1, where M is a non-negative integer. bmin is zero if emin is non-negative and −(bmax + K) otherwise.

When emin == emax == 0, this math also produces bmin == bmax == 0, by my count.

However, before you get your hopes up, see Footnote 96:

  1. This set of values is used to define promotion and conversion semantics for the enumeration type. It does not preclude an expression of enumeration type from having a value that falls outside this range.

I must admit it's not immediately obvious to me how to produce an expression of enumeration type with value outside the range. At least, static_cast to enumeration type exhibits undefined behavior if the value being cast falls outside the range.

Related