Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shouldn't the code print " 1 1" instead of "4 4"?

According to §7.2/5 and §7.2/6 shouldn't the code below print 1 1 instead of 4 4?

#include <iostream>
enum A { a = (char)1, b, c };   //  underlying type is not fixed

int main() {
    std::cout << sizeof(a) << ' ' << sizeof(A) << '\n';
}

Edit

From §7.2/5:

If the underlying type is not fixed, the type of each enumerator is the type of its initializing value:

— If an initializer is specified for an enumerator, the initializing value has the same type as the expression and the constant-expression shall be an integral constant expression (5.19).

like image 823
Wake up Brazil Avatar asked Apr 27 '14 12:04

Wake up Brazil


4 Answers

If you don't explicitly define the underlying type, then compiler is free to choose an integral type which fits the values. To set the underlying type in C++11 you can use this:

enum A : char { a = 1, b, c }; 
       ^^^^^^

Your way will not force the compiler to use char instead if int.

like image 171
masoud Avatar answered Nov 10 '22 13:11

masoud


This is implementation defined: the fact that all values of an enum fit in, say, a uint8_t does not force the compiler to pick a single-byte representation for the enumeration.

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 enumerator cannot fit in an int or unsigned int. (emphasis added)

In your case it appears that the compiler implementers choose an int, which takes four bytes on your platform - a perfectly valid choice.

like image 39
Sergey Kalinichenko Avatar answered Nov 10 '22 12:11

Sergey Kalinichenko


No. It has been the case ever since ANSI C that conforming compilers often use int to store enums, even when all the values are small.

Before you say this is insane and it should use the smallest type that works (which by the way GCC will do if you use __attribute__((packed))), think about ABI compatibility. If you release a library which uses an enum type, you would prefer that the size of that type not change. If all enums start life with 4 bytes, the likelihood is increased that simply relinking against an updated library will work.

like image 4
John Zwinck Avatar answered Nov 10 '22 11:11

John Zwinck


The clause you quote, 7.2/5, describes the types of the enumerators. But the enumerators only form part of the definition of the enumeration. The underlying type of the enumeration is large enough to hold the values all enumerators, subject to 7.2/6:

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.

So it is guaranteed that your underlying type is no larger than int (since int can represent 0, 1 and 2). It is true that the type of your first enumerator is char inside the enum definition, but all actual enum values are of type A. To actually control the underlying type, use the enum-base syntax (e.g. enum A : char), and to query it you can use the std::underlying_type trait.

If you actually would like to see the effect of the enumerator's type in the definition, you can try something like this:

enum Foo { a = '\010', b = sizeof(a) };

std::cout << typeid(b).name() << "\n";    // some variant of "Foo"
std::cout << b << "\n";                   // "1"
std::cout << sizeof(b) << "\n";           // implementation-defined, not greater
                                          // than sizeof(int)
like image 4
Kerrek SB Avatar answered Nov 10 '22 12:11

Kerrek SB