8

I often assume that size of an enumeration is the same as the size of its underlying type. But is it mandated by the standard?

The standard (C++14, n4296) says that every enumeration has an underlying type (7.2/5). The standard also says that objects are represented as sequences of bytes, and that the size of an object is related to its representation:

3.9/4 The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T).

5.3.3/1 The sizeof operator yields the number of bytes in the object representation of its operand.

However, I was not able to find any relation between an enum's underlying type and the object representation. Is there any? If not, than I would argue that sizeof of an enumeration does not have to be sizeof of its underlying type.

So my questions are:

  1. Is there any relation between an enum's underlying type and its object representation?

  2. Does the standard really require that sizeof(std::underlying_type_t<E>) == sizeof(E) for any enumeration E?

Jan Tušil
  • 958
  • 5
  • 16
  • I'm 99% sure that they have to be the same size. E.g. there's `std::byte`, which is just `enum class byte : unsigned char` (can't remember signed or unsigned), and it's guaranteed to be the same size. I'm not sure where it is in the standard, though. – Justin Nov 22 '17 at 21:31
  • What size would it be otherwise? –  Nov 22 '17 at 21:33
  • @NeilButterworth any larger? It has to be at least as large as the UT, because it has to store all its values. – Jan Tušil Nov 22 '17 at 21:35
  • I think there are arguments that say it could be smaller - for example, if an enum has a only two values, it can be stored in a char, but the specified underlying type might be an int. Note I'm _not_ saying this is what happens. –  Nov 22 '17 at 21:42
  • @NeilButterworth That may be true for plain old C enums (aka unscoped enums whose UT is not fixed) . – Jan Tušil Nov 22 '17 at 22:23
  • 1. It's in the standard. 2. It's in the standard. – Michaël Roy Nov 22 '17 at 23:07

2 Answers2

2

Taken from: What is the underlying type of a c++ enum?, An older C++ standard stated at 7.2/5:

The underlying type of an enumeration is an integral type that can represent all the enumerator values defined in the enumeration. It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enu- merator cannot fit in an int or unsigned int. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0. The value of sizeof() applied to an enu- meration type, an object of enumeration type, or an enumerator, is the value of sizeof() applied to the underlying type.

From draft n4606 the closest I could find is 7.2/7 + 8 which states:

7) For an enumeration whose underlying type is not fixed, the underlying type is an integral type that can represent all the enumerator values defined in the enumeration. If no integral type can represent all the enumerator values, the enumeration is ill-formed. It is implementation-defined which integral type is used as the underlying type except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0.

8) For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, 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, |emax|) and equal to 2M − 1, where M is a non-negative integer. bmin is zero if emin is non-negative and −(bmax + K) otherwise. The size of the smallest bit-field large enough to hold all the values of the enumeration type is max(M, 1) if bmin is zero and M + 1 otherwise. 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

On the one hand it seems close enough, on the other hand, the specific demand for the sizeof() operator was removed. Still I think it is safe enough to state that answer for both the questions is yes.

OriBS
  • 722
  • 5
  • 9
1

I've searched the standard, and this is best I could find (I know this may not be adequate):

Underlying type is defined implicitly here (3.9.1/5 of C++14):

Type wchar_t is a distinct type whose values can represent distinct codes for all members of the largest extended character set specified among the supported locales (22.3.1). Type wchar_t shall have the same size, signedness, and alignment requirements (3.11) as one of the other integral types, called its underlying type. Types char16_t and char32_t denote distinct types with the same size, signedness, and alignment as uint_least16_t and uint_least32_t, respectively, in <cstdint>, called the underlying types.

This definition is about wchar_t, but I think it's safe to say that that's the definition for underlying type. (at least, that's the best definition the standard gives. Besides, the index of C++14 standard for underlying type refers to here)

That means:

  1. the representation must be the same

  2. size must be equal

(But I do think that underlying type should be defined better in the standard)

geza
  • 28,403
  • 6
  • 61
  • 135
  • Thanks. I've found this too. It says "its underlying type", so i would say it only defines an underlying type for wchar_t in similar fashion to 7.2/5 for enums. – Jan Tušil Nov 22 '17 at 22:30
  • @JanTušil: yes, but the text differs a little bit. Here, it's "called its underlying type", so can be interpreted in a way meaning "underlying type has these attributes, that's the definition" (not just for wchar_t). For enums, they don't say this way. But yeah, one can interpret it like you say, too. The standard is unclear here, should be improved. Even if you accept this definition, the answer for 1. is not 100% precise, as nowhere is written that the underlying repr should be the same. But it would be very strange if it wasn't (why would it have the name "underlying" then?). – geza Nov 22 '17 at 22:33
  • I would say that this quote only defines "underlying type" for wchar_t – M.M Nov 22 '17 at 23:12
  • M.M: it's unclear for me. The fact that the index for "underlying type" refers here, supports this is the definition. On the other hand, it's written in `wchar_t` context, so it can define just for `wchar_t`. This definition should have its own bullet point. This should be clarified. But to be honest, I don't think that the definition for underlying type should differ for various types (and I hope this is the intention of the committee too). C++ is insanely complex, let's not complicate this further. – geza Nov 22 '17 at 23:20
  • Also, I don't think "same size, signedness and alignment requirements" guarantees identical representation. For example an enum might be big-endian while the underlying type is little-endian, and those three equivalences still hold. – M.M Jun 11 '20 at 00:21