C++ от C досталось тяжелое наследство. Одна его часть была исправлена и беспощадно зарезана для большей надежности — так, например, поступили с неявными const
преобразованиями. Другая же часть, доставляющая не меньше проблем, перешла в первозданном виде.
В C и C++ много различных типов целых чисел разных размеров. И над ними определены операции. Правда, операции определены не для каждого типа чисел.
Например, здесь нет +, -, *, / для uint16_t
. Но применить мы их можем.
uint16_t x = 1;
uint16_t y = 2;
auto a = x - y;
auto b = x + y;
auto c = x * y;
auto d = x / y;
и результатом операций над беззнаковыми числами станет число со знаком.
Но стоит тип хотя бы одного аргумента поменять на uint32_t
, как результат сразу же теряет знак.
Происходят две неявные операции:
- Типы, меньшие
int
, приводятся кint
(integer promotion). Знаковому! Независимо от знаковости исходного типа! - Когда в операции участвуют аргументы разных типов целых чисел, они приводятся к общему типу (usual arithmetic conversion):
- Меньший тип приводится к большему
- Если размеры одинаковы, то знаковый приводится к беззнаковому
Аналогичные операции проводятся и над числами с плавающей точкой. За полной таблицей и цепочкой, что и в кого неявно превращается, стоит обратиться к тексту стандарта.
- К ошибкам в логике:
Неявные преобразования вовлекаются в любую операцию. Вы выполняете сравнение знакового и беззнакового числа и забыли явно привести типы? Готовьтесь к тому, что
-1 < 1
может вернутьfalse
:std::vector<int> v = {1}; auto idx = -1; if (idx < v.size()) { std::cout << "less!\n"; } else { std::cout << "oops!\n"; }
- К неопределенному поведению:
Integer promotion неявно приводит
unsigned short x=0xFFFF; unsigned short y=0xFFFF; auto z=x*y;
x
иy
кint
, в котором происходит переполнение. Переполнениеint
— неопределенное поведение. - К трудностями в переносе программ с одной платформы на другую. Если меняется размер
int
/long
применение правил неявных конверсий к вашему коду также меняется:Выводит разные значения в зависимости от размера типаstd::cout << (-1L < 1U);
long
.
- Не смешивать в одном выражении знаковые и беззнаковые типы
- Уделять особое внимание коду, работающему с типами, меньшими
int
. - Включать предупреждения от компилятора (
-Wconversion
, не всегда работает)