Согласно §7.2 / 5 и §7.2 / 6 не следует код ниже печатать 1 1 вместо 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';
}

Изменить

Из §7.2 / 5:

Если базовый тип не фиксирован, тип каждого перечислителя является типом его инициализирующего значения:

- Если для перечислителя указан инициализатор, инициализирующее значение имеет тот же тип, что и выражение, а константное выражение должно быть интегральным константным выражением (5.19).

13
Wake up Brazil 27 Апр 2014 в 16:38

4 ответа

Лучший ответ

Если вы явно не определяете базовый тип, компилятор может выбрать интегральный тип, который соответствует значениям. Чтобы установить базовый тип в C ++ 11, вы можете использовать это:

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

Ваш способ не заставит компилятор использовать char вместо int.

20
masoud 27 Апр 2014 в 12:45

Это определяется реализацией: тот факт, что все значения enum вписываются, скажем, в uint8_t, не заставляет компилятор выбирать однобайтовое представление для перечисления.

Базовый тип перечисления - это интегральный тип, который может представлять все значения перечислителя, определенные в перечислении. Это определяется реализацией, какой интегральный тип используется в качестве базового типа для перечисления , за исключением того, что базовый тип не должен быть больше, чем int, если только значение перечислителя не может поместиться в int или unsigned int. (курсив добавлен)

В вашем случае кажется, что разработчики компилятора выбирают int, который занимает четыре байта на вашей платформе - совершенно правильный выбор.

5
Sergey Kalinichenko 27 Апр 2014 в 12:42

Нет. Начиная с ANSI C, соответствующие компиляторы часто используют int для хранения перечислений, даже когда все значения маленькие.

Прежде чем вы скажете, что это безумие и что он должен использовать наименьший тип, который работает (что, кстати, GCC будет делать, если вы используете __attribute__((packed))), подумайте о совместимости с ABI. Если вы выпускаете библиотеку, в которой используется перечисляемый тип, вы бы предпочли, чтобы размер этого типа не менялся. Если все перечисления начинают жизнь с 4 байтов, увеличивается вероятность того, что простое повторное связывание с обновленной библиотекой будет работать.

4
John Zwinck 27 Апр 2014 в 12:44

Цитируемый вами пункт 7.2 / 5 описывает типы счетчиков . Но счетчики являются только частью определения перечисления . Базовый тип перечисления достаточно велик, чтобы содержать значения всех перечислителей, в соответствии с 7.2 / 6:

Это определяется реализацией, какой интегральный тип используется в качестве базового типа, за исключением того, что базовый тип не должен быть больше, чем int, если значение перечислителя не может поместиться в int или unsigned int .

Таким образом, гарантируется, что ваш базовый тип не больше, чем int (поскольку int может представлять 0, 1 и 2). Верно, что тип вашего первого перечислителя - char внутри определения перечисления, но все фактические значения перечисления имеют тип A. Чтобы фактически контролировать базовый тип, используйте синтаксис enum-base (например, enum A : char), а для запроса его можно использовать трейт std::underlying_type.

Если вы действительно хотите увидеть эффект от типа перечислителя в определении, вы можете попробовать что-то вроде этого:

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)
4
Kerrek SB 27 Апр 2014 в 19:31