Обычные арифметические преобразования
Многие бинарные операторы, ожидающие операнды типа арифметический или перечисление, вызывают преобразования и дают типы результатов аналогичным образом. Цель состоит в получении общего типа, который также является типом результата. Этот шаблон называется обычными арифметическими преобразованиями.
Определение
Обычные арифметические преобразования определяются следующим образом:
Этап 1
Применяет преобразование lvalue в rvalue к обоим операндам, результирующие prvalue используются вместо исходных операндов для оставшейся части процесса.
Этап 2
| (с C++11) |
Этап 3
| (с C++26) |
Этап 4
- Если один из операндов имеет тип с плавающей точкой, применяются следующие правила:
- Если оба операнда имеют одинаковый тип, дальнейшее преобразование не требуется.
- В противном случае, если один из операндов имеет тип, не являющийся типом с плавающей точкой, этот операнд преобразуется к типу другого операнда.
- В противном случае, если ранги преобразования типов с плавающей точкой типов операндов упорядочены, но(с C++23) не равны, то операнд типа с меньшим рангом преобразования типа с плавающей точкой преобразуется к типу другого операнда.
| (с C++23) |
- В противном случае оба операнда имеют целочисленные типы, переходим к следующему этапу.
Этап 5
Оба операнда преобразуются к общему типу C. Учитывая типы T1 и T2 в качестве типа с продвижением (согласно правилам повышения целочисленных типов) операндов, применяются следующие правила для определения C:
- Если
T1иT2— это один и тот же тип, тоC— это этот тип. - В противном случае, если
T1иT2— это оба знакомые целочисленные типы или оба беззнаковые целочисленные типы,C— это тип с большим рангом целочисленного преобразования. - В противном случае один тип между
T1иT2— знаковый целочисленный типS, а другой — беззнаковый целочисленный типU. Применяются следующие правила: - Если ранг целочисленного преобразования
Uбольше или равен рангу целочисленного преобразованияS, тоC— этоU. - В противном случае, если
Sможет представить все значенияU, тоC— этоS. - В противном случае
C— это беззнаковый целочисленный тип, соответствующийS.
| Если один операнд имеет тип перечисления, а другой — тип перечисления или тип с плавающей точкой, это поведение устарело. |
(с C++20) (до C++26) |
Ранг целочисленного преобразования
Каждый целочисленный тип имеет целочисленный ранг, определенный следующим образом:
- Ни два знаковых целочисленных типа, кроме char и signed char (если char — знаковый), не имеют одинакового ранга, даже если у них одинаковое представление.
- Ранг знакового целочисленного типа больше ранга любого знакового целочисленного типа с меньшей шириной.
- Ранги следующих целочисленных типов уменьшаются в указанном порядке:
| (с C++11) |
- long
- int
- short
- signed char
- Ранг любого беззнакового целочисленного типа равен рангу соответствующего знакового целочисленного типа.
| (с C++11) |
- Ранг bool меньше ранга всех стандартных целочисленных типов.
- Ранги типов символьных кодировок (char ,
char8_t(с C++20),char16_t,char32_t,(с C++11) и wchar_t) равны рангам их базовых типов, что означает: - Ранг char равен рангу signed char и unsigned char.
| (с C++20) |
| (с C++11) |
- Ранг wchar_t равен рангу его реализационно-определенного базового типа.
| (с C++11) |
- Для всех целочисленных типов
T1,T2, иT3, еслиT1имеет больший ранг, чемT2иT2имеет больший ранг, чемT3, тоT1имеет больший ранг, чемT3.
Ранг целочисленного преобразования также используется в определении повышения целочисленных типов.
Ранг и подранг преобразования типов с плавающей точкой
Ранг преобразования типа с плавающей точкой
Каждый тип с плавающей точкой имеет ранг преобразования типа с плавающей точкой, определенный следующим образом:
- Ранги стандартных типов с плавающей точкой уменьшаются в указанном порядке:
- long double
- double
- float
| (с C++23) |
Подранг преобразования типа с плавающей точкойТипы с плавающей точкой, имеющие равные ранги преобразования типов с плавающей точкой, упорядочиваются по подрангу преобразования типа с плавающей точкой. Подранг формирует полное упорядочение среди типов с одинаковыми рангами. Типы | (с C++23) |
Использование
Ранг и подранг преобразования типа с плавающей точкой также используются для
- определить, может ли преобразование между разными типами с плавающей запятой быть неявным или является сужающим преобразованием,
- различать последовательности преобразований при разрешении перегрузки,
| (с C++23) |
- определить, является ли преобразование конструктор класса
std::complexявным или - определить общий тип с плавающей запятой, если в качестве аргументов функциям общего или специального математического назначения передаются аргументы разных типов с плавающей запятой.
Отчёты об ошибках
Следующие отчёты об ошибках, меняющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применён к | Поведение, как опубликовано | Правильное поведение |
|---|---|---|---|
| CWG 1642 | C++98 | обычные арифметические преобразования могут включать lvalues | сначала применяются преобразования lvalue в rvalue |
| CWG 2528 | C++20 | трёхстороннее сравнение между unsigned char и unsigned int некорректно, поскольку происходит промежуточное целочисленное продвижение[1] | определяет общий тип на основе продвинутых типов, без фактического продвижения операндов[2] |
- До решения,
unsigned charпродвинулся доintв начале стадии 5, а затем преобразуется вunsigned int. Однако последнее преобразование является сужающим, что делает трёхстороннее сравнение некорректным. - После решения, общий тип всё ещё
unsigned int. Разница заключается в том, чтоunsigned charнепосредственно преобразуется вunsigned intбез промежуточного целочисленного продвижения. Преобразование не является сужающим, и, следовательно, трёхстороннее сравнение корректно.
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/usual_arithmetic_conversions