# Integer promotion C++ от C досталось тяжелое наследство. Одна его часть была исправлена и беспощадно зарезана для большей надежности — так, например, поступили с неявными `const` преобразованиями. Другая же часть, доставляющая не меньше проблем, перешла в первозданном виде. В C и C++ много различных типов целых чисел разных размеров. И над ними определены операции. Правда, операции определены не для каждого типа чисел. Например, здесь нет +, -, *, / для `uint16_t`. Но применить мы их можем. ```C++ uint16_t x = 1; uint16_t y = 2; auto a = x - y; auto b = x + y; auto c = x * y; auto d = x / y; ``` и [результатом](https://godbolt.org/z/bdKjsor9T) операций над беззнаковыми числами станет число со знаком. Но стоит тип хотя бы одного аргумента поменять на `uint32_t`, как результат сразу же [теряет знак](https://godbolt.org/z/aY1nhdr37). ## Что происходит? Происходят две неявные операции: 1. Типы, меньшие `int`, приводятся к `int` (integer promotion). Знаковому! Независимо от знаковости исходного типа! 2. Когда в операции участвуют аргументы разных типов целых чисел, они приводятся к общему типу (usual arithmetic conversion): - Меньший тип приводится к большему - Если размеры одинаковы, то знаковый приводится к беззнаковому Аналогичные операции проводятся и над числами с плавающей точкой. За полной таблицей и цепочкой, что и в кого неявно превращается, стоит обратиться к тексту [стандарта](https://eel.is/c++draft/conv.rank). ## К чему это приводит? 1. К ошибкам в логике: Неявные преобразования вовлекаются в любую операцию. Вы выполняете сравнение знакового и беззнакового числа и забыли явно привести типы? Готовьтесь к тому, что `-1 < 1` может [вернуть](https://godbolt.org/z/sqvrasjE4) `false`: ```C++ std::vector v = {1}; auto idx = -1; if (idx < v.size()) { std::cout << "less!\n"; } else { std::cout << "oops!\n"; } ``` 2. К [неопределенному поведению](https://godbolt.org/z/M3Kx3e3q6): ```C++ unsigned short x=0xFFFF; unsigned short y=0xFFFF; auto z=x*y; ``` Integer promotion неявно приводит `x` и `y` к `int`, в котором происходит переполнение. Переполнение `int` — неопределенное поведение. 3. К трудностями в переносе программ с одной платформы на другую. Если меняется размер `int`/`long` применение правил неявных конверсий к вашему коду также [меняется](https://godbolt.org/z/hs59o3zca): ```C++ std::cout << (-1L < 1U); ``` Выводит разные значения в зависимости от размера типа `long`. ## Что делать? 1. Не смешивать в одном выражении знаковые и беззнаковые типы 2. Уделять особое внимание коду, работающему с типами, меньшими `int`. 3. Включать предупреждения от компилятора (`-Wconversion`, не всегда работает) ## Полезные ссылки 1. https://eel.is/c++draft/conv.prom 2. https://eel.is/c++draft/expr.arith.conv 3. https://stackoverflow.com/questions/46073295/implicit-type-promotion-rules 4. https://shafik.github.io/c++/2021/12/30/usual_arithmetic_confusions.html