Что произойдет, если значение static_cast недопустимо для класса enum?

Рассмотрим этот код С++ 11:

enum class Color : char { red = 0x1, yellow = 0x2 }
// ...
char *data = ReadFile();
Color color = static_cast<Color>(data[0]);

Предположим, что данные [0] на самом деле 100. Что такое цвет, установленный в соответствии со стандартом? В частности, если я позже сделаю

switch (color) {
    // ... red and yellow cases omitted
    default:
        // handle error
        break;
}

Стандарт гарантирует, что значение по умолчанию будет удалено? Если нет, то какой правильный, самый эффективный, самый элегантный способ проверить ошибку здесь?

EDIT:

Как бонус, делает ли стандарт какие-либо гарантии по этому поводу, но с простым перечислением?

Ответ 1

Что такое цвет в соответствии со стандартом?

Ответ с цитатой из Стандарта:

[expr.static.cast]/10

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

Посмотрите на диапазон значений перечисления: [dcl.enum]/7

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

До CWG 1766 Следовательно, для data[0] == 100 результирующее значение указывается (*), а не Undefined Поведение (UB). В более общем плане, когда вы отбрасываете базовый тип в тип перечисления, никакое значение в data[0] не может привести к UB для static_cast.

После CWG 1766 См. дефект CWG 1766. Параграф [expr.static.cast] p10 был усилен, поэтому теперь do вызывать UB, если вы выставляете значение, выходящее за пределы отображаемого диапазона перечисления, к типу перечисления.

(*) char требуется шириной не менее 8 бит, но не требуется unsigned. Максимальное сохраняемое значение должно быть не менее 127 за приложение E стандарта C99.


Сравните с [expr]/4

Если во время оценки выражения результат не определен математически или нет в диапазоне отображаемых значений для его типа, поведение undefined.

Таким образом, это может легко привести к UB, если значение было достаточно большим, например. static_cast<Color>(10000);.


Теперь оператор switch:

[stmt.switch]/2

Условие должно быть целочисленного типа, типа перечисления или типа класса. [...] Выполняются интегральные рекламные акции.

[conv.prom]/4

Значение типа без подписки, базовый тип которого фиксирован (7.2), может быть преобразован в prvalue его базового типа. Более того, если интегральное продвижение может быть применено к его базовому типу, то присвоение неперечисленного типа перечисления, базовый тип которого является фиксированным, также может быть преобразовано в prvalue продвинутого базового типа.

Примечание. Основной тип области с enum без enum-base - int. Для неперечисленных перечислений базовый тип определяется реализацией, но не должен превышать int, если int может содержать значения всех счетчиков.

Для неперечисленного перечисления это приводит к тому, что /1

Значение целочисленного типа, отличного от bool, char16_t, char32_t или wchar_t, чей целочисленный ранг преобразования (4.13) меньше ранга int, может быть преобразован в prvalue type int, если int может представлять все значения типа источника; в противном случае исходное prvalue может быть преобразовано в prvalue типа unsigned int.

В случае перечисления без подписки здесь мы будем иметь дело с int. Для перечислений (enum class и enum struct) интегральная акция не применяется. В любом случае интегральное продвижение также не приводит к UB, поскольку сохраненное значение находится в диапазоне базового типа и в диапазоне int.

[stmt.switch]/5

Когда выполняется оператор switch, его состояние оценивается и сравнивается с каждой константой случая. Если одна из констант случая равна значению условия, управление передается в оператор, следующий за соответствующей меткой case. Если константа case соответствует условию, и если есть метка default, управление переходит к выражению, помеченному меткой default.

Надпись default должна быть удалена.

Примечание. Можно было бы еще раз взглянуть на оператор сравнения, но он явно не используется в упомянутом "сравнении". На самом деле, нет никакого намека на то, что в нашем случае будет введен UB для областей с ограниченным или неперечисленным перечислением.


Как бонус, делает ли стандарт какие-либо гарантии по этому поводу, но с простым перечислением?

Является ли область enum областью действия, здесь не имеет никакого значения. Однако имеет значение, является ли основной тип фиксированным. Полный [decl.enum]/7:

Для перечисления, основной тип которого является фиксированным, значения перечисления являются значениями базового типа. В противном случае для перечисления, где e min является наименьшим перечислителем, а e max является наибольшим, значения перечисления являются значениями в диапазоне b min до b max, определяемый следующим образом: пусть K be 1 для двухкомпонентного представления и 0 для представления одного дополнения или знака. b max - наименьшее значение, большее или равно max (| e min | - K, | e max |) и равно до 2 M - 1, где M - неотрицательное целое число. b min равно нулю, если e min неотрицательно и - (b max + K) в противном случае.

К счастью, ваш наименьший перечислитель перечисляет red = 0x1, поэтому max (| e min | - K, | e max |) равен | e <югу > тахсуб > | в любом случае, который равен yellow = 0x2. Наименьшее значение, большее или равное 2, равное 2 M - 1 для положительного целого M, есть 3 (2 2 - 1), (Я думаю, что цель состоит в том, чтобы разрешить диапазон до 1-битного шага.) Отсюда следует, что b max 3 и bmin есть 0.

Следовательно, 100 будет находиться за пределами диапазона перечисления, а static_cast создаст неуказанное значение, которое может привести к UB согласно [expr]/4..