Поведение инициализации значения перечисления

Во-первых, я хочу сказать, что, согласно cppreference.com, инициализировать перечисление несколько невозможно.

Согласно http://en.cppreference.com/w/cpp/language/value_initialization, инициализация значения enum фактически выполняет нулевую инициализацию. Из этого следует, что, согласно http://en.cppreference.com/w/cpp/language/zero_initialization, эффект инициализации нуля перечисления:

Если T является скалярным типом, начальное значение объекта - это целая постоянная ноль, неявно преобразуемая в T,

Тем не менее, целочисленная константа ноль не может быть неявно преобразована в перечисление. В конечном счете, перечисление не может быть инициализировано значением. Это звучит странно, и инициализация значения enum работает на VC, GCC и Clang. Итак, что стандарт говорит об этом?

Во-вторых, согласно http://en.cppreference.com/w/cpp/language/static_cast:

Целочисленный тип, тип с плавающей точкой или тип перечисления может быть преобразован в любой полный тип перечисления (результат не определен (до C++17), неопределенное поведение (начиная с C++17), если значение выражения преобразовано в базовый тип перечисления, не является одним из целевых значений перечисления)

Итак, означает ли это, что инициализация значения перечисления (если оно вообще работает) может фактически привести к неопределенному поведению, если целевое перечисление не имеет перечислителя, равного 0?

2 ответа

Ответ на это был дан в комментариях. Моя попытка объяснить весь стандарт за этим дана ниже.

Для инициализации нуля объекта или ссылки типа T средства:

  • если T является скалярным типом (3.9), объект инициализируется значением, полученным путем преобразования целочисленного литерала 0 (ноль) до T;

(Перечисления являются скалярными типами; §3.9/9) Так как преобразование не называется неявным, мы не смотрим в §4, а в §5.2.9;

Результат выражения static_cast<T>(v) является результатом преобразования выражения v печатать T,

Затем в §5.2.9/10 определяется, как целочисленные значения преобразуются в типы перечисления.

Значение целочисленного типа или типа перечисления может быть явно преобразовано в тип перечисления. Значение не изменяется, если исходное значение находится в диапазоне значений перечисления (7.2). В противном случае результирующее значение не определено (и может не входить в этот диапазон).

Должно быть показано, что ноль находится в диапазоне значений перечисления для всех перечислений.
Следующие пять цитат взяты из §7.2/8:

Для перечисления, базовый тип которого является фиксированным, значения перечисления являются значениями базового типа.

Поскольку все разрешенные базовые типы включают ноль в свой диапазон значений *, это автоматически дает желаемый результат. Теперь для перечислений без фиксированных базовых типов,

В противном случае для перечисления, где emin является наименьшим перечислителем, а e max является наибольшим, значения перечисления являются значениями в диапазоне от b min до b max, определяемыми следующим образом:

Т.е. мы должны показать, что bmin всегда меньше или равно нулю, а bmax всегда больше или равно нулю.

Пусть K будет 1 для представления дополнения до двух и 0 для представления дополнения или величины знака.
b max - это наименьшее значение, большее или равное max (| e min| - K, | e max|) и равное 2M - 1, где M - неотрицательное целое число.

| макс| является неотрицательным, и максимум двух чисел, по крайней мере, так же велик, как оба числа. Следовательно, max (| e min| - K, | e max|) также неотрицательна, и bmax должно быть больше или равно этому числу - поэтому наше первое требование выполнено.

b min равно нулю, если emin неотрицательно, и - (bmax + K) в противном случае.

bmin явно равен нулю или отрицателен: bmax неотрицателен, как показано выше, а K неотрицателен (0 или 1), следовательно, аддитивная обратная их сумма не положительна. Наше второе требование выполнено. В заключение,

Если список перечислителя пуст, значения перечисления такие, как если бы перечисление имело единственный перечислитель со значением 0,

Это приводит к приведенному выше результату, устанавливая emin = emax = 0.


  • Это сводится к утверждению "Все целочисленные типы имеют ноль в диапазоне значений", что остается доказать читателю.

1: Это можно понять так:

enum class SomeEnum : int { V1 = 0, V2 = 1, V3 = 2 }; 
SomeEnum a = 0; // compile error
SomeEnum b = SomeEnum.V1; // OK

Это базовая защита от неопределенного поведения!

2: Да и Да:)

SomeEnum c = static_cast<SomeEnum>(1); // = SomeEnum.V2
SomeEnum d = static_cast<SomeEnum>(5); // undefined behavior

static_cast опасен по определению, его следует использовать только для поддержки сериализации или старых интерфейсов c!

Другие вопросы по тегам