>
>
>
V1016. The value is out of range of enu…


V1016. The value is out of range of enum values. This causes unspecified or undefined behavior.

Анализатор обнаружил опасное приведение числа к перечислению. Указанное число может не входить в диапазон значений 'enum'.

Примечание 1: Данное правило актуально только для языка C++. В качестве нижележащего типа 'enum' в языке C всегда используется тип 'int'.

Примечание 2: Данное правило актуально только для C++ компиляторов, рассчитывающих фактический размер 'enum' согласно стандарту. Например, такими компиляторами являются GCC и Clang. MSVC не является таким компилятором, так как рассчитывает размер 'enum' в целях обратной совместимости по правилам языка C и в качестве нижележащего типа всегда использует тип 'int', если специально не указан иной тип.

Результат приведения числа, чьё значение не входит в диапазон элементов 'enum', является неуточненным поведением до C++17 и неопределенным поведением, начиная с C++17.

Если у 'enum' указан нижележащий тип, то к типу этого 'enum' можно приводить все значения, которые можно уместить в этот тип.

Пример 1:

enum class byte : unsigned char {}; // Range: [0; 255]
byte b1 = static_cast<byte>(255);   // ok

Число 256 уже не вмещается в тип 'char', и этот код некорректен:

byte b2 = static_cast<byte>(256);   // UB

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

int -> unsigned int -> long -> unsigned long ->
  long long -> unsigned long long

При этом внутри выбранного типа компилятор использует минимально необходимое число бит (n), способное уместить максимальное число в перечислении. В такой 'enum' можно уместить диапазон значений [- (2^n) / 2; 2^n / 2 - 1] для 'enum' со знаковым нижележащим типом и [0; 2^n - 1] для 'enum' с беззнаковым нижележащим типом. Выход за границы этого диапазона является неуточненным поведением (до C++17) или неопределенным поведением (с C++17).

Пример 2:

enum foo { a = 0, b = UINT_MAX }; // Range: [0; UINT_MAX]
foo x = foo(-1);                  // UB

На первый взгляд, такой код является корректным, но на самом деле он может приводить к проблемам. Нижележащим типом 'enum' выбирается 'unsigned int'. Число '-1' не попадает в диапазон этого типа, поэтому такое присвоение может привести к неуточнённому или неопределённому поведению.

Пример 3:

enum EN { low = 2, high = 4 }; // Uses 3 bits, range: [0; 7]
EN a1 = static_cast<EN>(7);    // ok

Согласно стандарту нижележащим типом для этого enum выберется 'int'. Внутри этого типа компилятор использует минимальное количество битовых полей, которое сможет вместить в себя все значения enum-констант.

В данном случае для вмещения всех значений (2 = 0b010 и 4 = 0b100) понадобится минимум 3 бита, поэтому переменная типа EN может вместить в себя числа от 0 (0b000) до 7 (0b111) включительно. Число 8 занимает уже четыре бита (0b1000), поэтому в тип EN оно уже не вмещается:

EN a2 = static_cast<EN>(8);    // UB

UndefinedBehaviorSanitizer также находит ошибку в этом примере: https://godbolt.org/z/GGYo7z.

При этом, если для EN указать нижележащий тип, например 'unsigned char', то это уже будет корректный вариант кода:

enum EN : unsigned char { low = 2, high = 4 }; // Range: [0; 255]
EN a2 = static_cast<EN>(8);                    // ok

Данная диагностика классифицируется как: