Во-первых, я хочу сказать, что, согласно 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
?
Ответ на это был дан в комментариях. Моя попытка объяснить весь стандарт за этим дана ниже.
к нулевой инициализации объект или ссылка типа
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:
Для перечисления, базовый тип которого является фиксированным, значения
перечисления являются значениями базового типа.
Поскольку все разрешенные базовые типы включают ноль в свой диапазон значений *, это автоматически дает желаемый результат. Теперь для перечислений без фиксированных базовых типов,
В противном случае для перечисления, где емин самый маленький
счетчик и е Максимум является наибольшим, значения
перечисление значения в диапазоне б мин в б
Максимум , определяется следующим образом:
То есть мы должны показать, что бмин всегда меньше или равно нулю, и бМаксимум всегда больше или равно нулю.
Позволять К быть 1 для двоих
дополнение представления и 0 для дополнения или
представление величины знака.
б Максимум наименьшее значение больше или равно тах (| е мин| — К, | е Максимум|) и равно 2М — 1, где
M неотрицательное целое число
| е Максимум| является неотрицательным, и максимум двух чисел, по крайней мере, так же велик, как оба числа. следовательно тах (| е мин| — К, | е Максимум|) также неотрицателен, и бМаксимум должно быть больше или равно этому числу — поэтому наше первое требование выполнено.
б мин ноль, если емин неотрицателен и — (бМаксимум + K) иначе.
бмин явно либо ноль, либо отрицательный: бМаксимум является неотрицательным, как показано выше, и К неотрицательно (0 или 1), следовательно, аддитивная обратная их сумма не положительна. Наше второе требование выполнено. В заключение,
Если переписчик-лист пусто, значения
перечисления, как если бы перечисление имел один перечислитель с
значение0
,
Это приводит к приведенному выше результату путем установки емин = еМаксимум = 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!