>
>
>
V1084. The expression is always true/fa…


V1084. The expression is always true/false. The value is out of range of enum values.

Анализатор обнаружил странное сравнение переменной перечисления с числом. Указанное число не входит в диапазон значений перечисления, поэтому такое сравнение не имеет смысла.

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

Рассмотрим следующий пример:

enum byte : unsigned char {}; // Range: [0; 255]
void foo(byte b1)
{
  if (b1 == 256)   // logic error : always false
  {
    //....
  }
}

Перечисление 'byte' имеет нижележащий тип 'unsigned char'. Число 256 не вмещается в тип 'unsigned char', поэтому сравнение 'b1 == 256' всегда ложное.

Пример корректного сравнения:

enum byte : unsigned char {}; // Range: [0; 255]
void foo(byte b1)
{
  if (b1 == 255)   // ok
  {
    //....
  }
}

Более сложным случаем является перечисление без явного указания нижележащего типа.

Для языка C компилятор всегда использует тип 'int' в качестве нижележащего типа. Диапазоном значений перечисления будет весь диапазон 'int'.

Для языка C++ компилятор подставит в качестве нижележащего типа 'int' для строго типизированных перечислений (scoped enum). Диапазоном значений такого перечисления также будет весь диапазон 'int'.

Для обычных перечислений вычисление диапазона значений и нижележащего типа перечисления происходит особым образом. Согласно стандарту С++, компилятор выведет нижележащий тип на основе значений констант перечисления, пытаясь уместить их в следующие типы:

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

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

Поэтому следующий код на языке C++ содержит ошибку, если используется компилятор, отличный от MSVC (например, GCC или Clang):

enum EN { low = 2, high = 4 }; // Uses 3 bits, range: [0; 7]
void foo(EN en1)
{
  if (en1 != 8)   // logic error : always true
  {
    //....
  }
}

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

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

enum EN : int32_t { low = 2, high = 4 };
// Now range is: [−2 147 483 648, 2 147 483 647]
void foo(EN en1)
{
  if (en1 != 8)   // ok
  {
    //....
  }
}

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

enum EN { low = 2, high = 4 };
// MSVC will use int as underlying type
// range is: [−2 147 483 648, 2 147 483 647]

void foo(EN en1)
{
  if (en1 != 8)   // no logic error
  {
    //....
  }
}

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

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

//-V1084_TURN_OFF_ON_MSVC

Предупреждения V1084, актуальные для MSVC, останутся.

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