Expressions and statements
ES.46
Избегайте сужающих (потерь, усечений) арифметических преобразований
Причина
Сужающее преобразование уничтожает информацию, часто неожиданным образом.
Пример (плохой)
Ключевой пример — базовое сужение:
double d = 7.9;
int i = d; // плохо: сужение: i становится 7
i = (int) d; // плохо: мы заявим, что это всё ещё недостаточно явно
void f(int x, long y, double d)
{
char c1 = x; // плохо: сужение
char c2 = y; // плохо: сужение
char c3 = d; // плохо: сужение
}
Примечание
Библиотека поддержки рекомендаций предлагает операцию narrow_cast для указания того, что сужение допустимо, и narrow («сужение если»), которая бросает исключение, если сужение выбрасывает допустимые значения:
i = gsl::narrow_cast<int>(d); // OK (вы сами этого хотели): сужение: i становится 7
i = gsl::narrow<int>(d); // OK: бросает narrowing_error
Мы также включаем потерянные арифметические приведения, такие как из отрицательного типа с плавающей точкой в беззнаковый целочисленный тип:
double d = -7.9;
unsigned u = 0;
u = d; // плохо: сужение
u = gsl::narrow_cast<unsigned>(d); // OK (вы сами этого хотели): u становится 4294967289
u = gsl::narrow<unsigned>(d); // OK: бросает narrowing_error
Примечание
Это правило не применяется к контекстным преобразованиям в bool:
if (ptr) do_something(*ptr); // OK: ptr используется как условие
bool b = ptr; // плохо: сужение
Контроль
Хороший анализатор может обнаружить все сужающие преобразования. Однако отметка всех сужающих преобразований приведёт к большому количеству ложных срабатываний. Предложения:
- Отмечайте все преобразования из типов с плавающей точкой в целочисленные. (Возможно, только
float->charиdouble->int. Здесь будь осторожен! Нужны данные.) - Отмечайте все
long->char. (Подозреваю, чтоint->charочень распространён. Здесь будь осторожен! Нужны данные.) - Считайте сужающие преобразования для аргументов функции особенно подозрительными.