Поведение инициализации значения перечисления
Во-первых, я хочу сказать, что, согласно 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!