Що станеться, якщо для перерахунку класу static_cast недійсне значення для перерахунку класу?


146

Розглянемо цей код C ++ 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;
}

чи стандартна гарантія того, що буде встановлено дефолт? Якщо ні, то який правильний, найефективніший, найелегантніший спосіб перевірити наявність помилок тут?

Редагувати:

Як бонус, стандарт дає будь-які гарантії щодо цього, але з простим перерахуванням?

Відповіді:


131

Який колір встановлений відповідно до стандарту?

Відповідаючи цитатою зі стандартів C ++ 11 та C ++ 14:

[expr.static.cast] / 10

Значення інтегрального або типу перерахування може бути явно перетворене в тип перерахування. Значення є незмінним, якщо вихідне значення знаходиться в межах значень перерахування (7.2). В іншому випадку отримане значення не визначено (і може бути не в цьому діапазоні).

Давайте подивимось діапазон значень перерахування : [dcl.enum] / 7

Для перерахунку, базовий тип якого фіксований, значення перерахування є значеннями базового типу.

Перед CWG 1766 (C ++ 11, C ++ 14) Тому для data[0] == 100, отримане значення задається (*), і не визначене поведінка (UB) не бере участь. Більш загально, якщо ви переходите від базового типу до типу перерахування, жодне значення у не data[0]може призвести до UB для static_cast.

Після CWG 1766 (C ++ 17) Див. Дефект CWG 1766 . Абзац p10 [expr.static.cast] p10 був посилений, тому тепер ви можете викликати UB, якщо ви передаєте значення, яке знаходиться за межами репрезентативного діапазону enum, до типу enum. Це все ще не стосується сценарію у питанні, оскільки data[0]є основним типом перерахування (див. Вище).

Зверніть увагу, що CWG 1766 вважається дефектом у Стандарті, тому прийнято, щоб реалізатори компілятора застосовувались до їхніх режимів компіляції C ++ 11 та C ++ 14.

(*) charповинен бути принаймні 8 бітовим, але не обов'язково бути unsigned. Максимальне зберігається значення повинно бути принаймні 127відповідно до Додатку Е до стандарту C99.


Порівняйте з [expr] / 4

Якщо під час оцінки виразу результат математично не визначений або не знаходиться в діапазоні представлених значень для його типу, поведінка не визначена.

До CWG 1766, тип інтегрального перетворення -> тип перерахування може створювати не визначене значення . Питання полягає в тому, чи може невказане значення бути поза репрезентативними значеннями для його типу? Я вважаю, що відповідь є ні - якби відповідь була так , не було б різниці в гарантіях, які ви отримуєте для операцій з підписаними типами між "ця операція видає не визначене значення" та "ця операція має невизначене поведінку".

Отже, перед РГС 1766, навіть static_cast<Color>(10000)б НЕ Invoke UB; але після того, як РГС 1766, він робить Invoke UB.


Тепер, switchзаява:

[stmt.switch] / 2

Умова має бути інтегральним, перелічувальним або класовим. [...] Виконуються інтегральні акції.

[конв.пром] / 4

Первозначне значення непокритого типу перерахунку, базовий тип якого фіксований (7.2), може бути перетворений у первісне значення його базового типу. Більше того, якщо інтегральне просування може бути застосоване до його базового типу, первісне значення незафіксованого типу перерахунку, базовий тип якого фіксований, також може бути перетворений у первинну величину промоторованого базового типу.

Примітка: Основним типом обширного перерахунку без опорної бази є int. Для невизначених перерахунків базовий тип визначається реалізацією, але не повинен бути більшим, ніж intякщо він intможе містити значення всіх перелічувачів.

Для несхваленого перерахування це призводить нас до / 1

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

У випадку незгаданого перерахування ми б тут мали справу з ints. Для перелічених масштабів ( 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буде 1представлення комплементу двох та 0a відображення доповнення або знака. b max - найменше значення, що перевищує або дорівнює max (| e min | - K, | e max |) і дорівнює 2M - 1 , деMє невід’ємним цілим числом. b min дорівнює нулю, якщо e min неотрицательно, і - (b max + K) в іншому випадку.

Давайте подивимось на наступне перерахування:

enum ColorUnfixed /* no fixed underlying type */
{
    red = 0x1,
    yellow = 0x2
}

Зауважте, що ми не можемо визначити це як перерахований перелік, оскільки всі перераховані перерахунки мають фіксований тип.

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

Таким чином, він 100знаходився б поза межами діапазону ColorUnfixed, і static_castстворював би не визначене значення до CWG 1766 та невизначене поведінку після CWG 1766.


3
Базовий тип є фіксованим, тому діапазон значень перерахування (§ 7.2 [dcl.enum] p7) є "значеннями базового типу". 100, безумовно, є значенням char, тому "значення не змінюється, якщо вихідне значення знаходиться в межах діапазонних значень перерахування (7.2)." застосовується.
Кейсі

2
Довелося шукати, щоб шукати, що означає "UB". ("невизначена поведінка") У запитанні не було зазначено можливості невизначеної поведінки; тож мені не прийшло в голову, що ти можеш про це говорити.
karadoc

2
@karadoc Я додав посилання під час першого появи терміну.
dyp

1
Люблю цю відповідь. Для тих, хто скидається занадто швидко, зауважте, що останнє речення "Отже, 100 було б поза діапазоном ..." застосовується лише в тому випадку, якщо код був змінений для видалення базової специфікації типу (char у цьому випадку). Я думаю, що це все-таки малося на увазі.
Ерік Сеппанен

1
@Ruslan CWG 1766 (або його резолюція) не є частиною C ++ 14, але я думаю, що це буде частиною C ++ 17. Навіть з правилами C ++ 17 я не зовсім розумію, що ви маєте на увазі під "недійсним подальшим текстом вашої відповіді". Інші частини моєї відповіді в основному стосуються того, що "діапазон значень перерахування" - це те, на яке посилається expr.static.cast p10.
dyp
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.