Подтвердить что ты не робот

Что произойдет, если перечисление не может вписаться в неподписанный интегральный тип?

В соответствии с запросом Bathsheba и в качестве последующего вопроса "Что произойдет, если перечисление не может вписаться в интегральный тип?" :

Агрегат перечисления определяется следующим образом:

enum foo : unsigned int
{
    bar = UINT_MAX,
    oops
};

Определено ли значение oops или нет?


MSVS2015 компиляция:

warning C4340: 'oops': value wrapped from positive to negative value
warning C4309: 'initializing': truncation of constant value
warning C4369: 'oops':  enumerator value '4294967296' cannot be represented as 'unsigned int', value is '0'

Выход MSVS2015:

bar = 4294967295
oops= 0

gcc 4.9.2 компиляция:

9 : note: in expansion of macro 'UINT_MAX'
bar = UINT_MAX,
^
10 : error: enumerator value 4294967296l is outside the range of underlying type 'unsigned int'
oops
^
Compilation failed

gcc 4.9.2 output

//compilation failed
4b9b3361

Ответ 1

Это очень интересный вопрос. Простой ответ заключается в том, что это буквально undefined: стандарт ничего не говорит об этом случае.

Чтобы иметь лучший пример, рассмотрим этот enum:

 enum foo : bool { True=true, undefined };

В соответствии со стандартом:

[dcl.enum]/2: [...] Определение перечислителя без инициализатора дает перечислителю значение, полученное путем увеличения значения предыдущего перечислителя на единицу.

Следовательно, значение foo::undefined в нашем примере равно 2 (true+1). Который не может быть представлен как bool.

Он плохо сформирован?

Нет, в соответствии со стандартом, это совершенно верно, только нефиксированный базовый тип имеет ограничение на невозможность представления всех значений счетчика:

[dcl.enum]/7: для перечисления, основной тип которого не фиксирован, [...] Если интегральный тип не может представлять все значения счетчика, перечисление плохо сформировано.

В нем ничего не говорится о фиксированном базовом типе, который не может представлять все значения счетчика.

Каково значение исходного вопроса oops и undefined?

Это undefined: стандарт ничего не говорит об этом случае.

Возможные значения для foo::undefined:

  • Максимальное возможное значение (true): undefined и oops должно быть максимальным значением базового типа.
  • Наименьшее возможное значение (false): минимальное значение базового типа. Примечание. В целых числах со знаком это не будет соответствовать текущему поведению для переполнения Integer (undefined).
  • Случайное значение (?): компилятор выберет значение.

Проблема со всеми этими значениями состоит в том, что она может привести к двум полям с одинаковым значением (например, foo::True == foo::undefined).

Разница между инициализатором (например, undefined=2) и "неявным" инициализатором (например, True=true, undefined)

В соответствии со стандартом:

[dcl.enum]/5: Если базовый тип является фиксированным, тип каждого перечислителя перед замыкающей скобкой является базовым типом, а константное выражение в определении перечисления должно быть преобразованным постоянным выражением базовый тип.

Другими словами:

 enum bar : bool { undefined=2 };

эквивалентно

 enum bar : bool { undefined=static_cast<bool>(2) };

И тогда bar::undefined будет true. В "неявном" инициализаторе это не так: этот стандартный параграф говорит об этом только инициализаторе, а не о "неявном" инициализаторе.

Резюме

  • Таким образом, возможно, что enum с фиксированным базовым типом имеют недопредставленные значения.
  • Их значение undefined по стандарту.

В соответствии с вопросом и комментариями это недействительно в GCC и clang, но актуально для MSVS-2015 (с предупреждением).