Обычные арифметические преобразования
Многие бинарные операторы, ожидающие операнды арифметического типа (arithmetic) или перечислимого типа, выполняют преобразования и формируют результирующие типы сходным образом. Цель состоит в том, чтобы получить общий тип, который также является типом результата. Этот шаблон называется обычными арифметическими преобразованиями.
Определение
Обычные арифметические преобразования определяются следующим образом:
Этап 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 и signedchar (если char является знаковым), не имеют одинаковый ранг, даже если их представления совпадают.
- Ранг знакового целочисленного типа выше ранга любого знакового целочисленного типа меньшей ширины.
- Ранги следующих целочисленных типов убывают в указанном порядке:
| (начиная с C++11) |
- long
- int
- short
- signedchar
- Ранг любого беззнакового целочисленного типа равен рангу соответствующего знакового целочисленного типа.
| (начиная с C++11) |
- Ранг bool ниже ранга всех стандартных целочисленных типов.
- Ранги кодирующих символьных типов (char , char8_t(начиная с C++20), char16_t, char32_t,(начиная с C++11) и wchar_t) равны рангам их базовых типов, что означает следующее:
- Ранг char равен рангам signedchar и unsignedchar.
| (начиная с C++20) |
| (начиная с C++11) |
- Ранг wchar_t равен рангу его базового типа, определяемого реализацией.
| (начиная с C++11) |
- Для всех целочисленных типов
T1,T2иT3, если рангT1выше рангаT2, а рангT2выше рангаT3, то рангT1выше рангаT3.
Ранг целочисленного преобразования также используется в определении целочисленного продвижения.
Ранг и подранг преобразования типов с плавающей точкой
Ранг преобразования типов с плавающей точкой
Для каждого типа с плавающей точкой определён ранг преобразования типов с плавающей точкой следующим образом:
- Ранги стандартных типов с плавающей точкой убывают в указанном порядке:
- longdouble
- double
- float
| (начиная с C++23) |
Подранг преобразования типов с плавающей точкойТипы с плавающей точкой, имеющие одинаковые ранги преобразования, упорядочиваются по подрангу преобразования типов с плавающей точкой. Подранг задаёт полный порядок среди типов с одинаковыми рангами. Типы | (начиная с C++23) |
Использование
Ранг и подранг преобразования типов с плавающей точкой также используются для следующих целей:
- определения того, может ли преобразование между различными типами с плавающей точкой быть неявным или является ли оно сужающим преобразованием,
- различения последовательностей преобразований при разрешении перегрузки,
| (начиная с C++23) |
- определения того, является ли преобразующий конструктор
std::complexявным, или - определения общего типа с плавающей точкой, если аргументы разных типов с плавающей точкой передаются в общие или специальные математические функции.
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были ретроактивно применены к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение согласно опубликованному стандарту | Исправленное поведение |
|---|---|---|---|
| CWG 1642 | C++98 | обычные арифметические преобразования могли включать lvalue | сначала применяются преобразования из lvalue в rvalue |
| CWG 2528 | C++20 | трёхстороннее сравнение между unsignedchar и unsignedint некорректно из-за промежуточного целочисленного продвижения[1] | общий тип определяется на основе продвинутых типов, без фактического продвижения операндов[2] |
| CWG 2892 | C++98 | когда оба операнда имеют один и тот же тип с плавающей точкой, значение выражения «дальнейшее преобразование не требуется» было неясно | заменено на «дальнейшее преобразование не будет выполняться» |
- До принятия решения unsignedchar продвигался до int в начале этапа 5, а затем преобразовывался в unsignedint. Однако последнее преобразование является сужающим, что делает трёхстороннее сравнение некорректным.
- После принятия решения общим типом по-прежнему является unsignedint. Разница состоит в том, что unsignedchar напрямую преобразуется в unsignedint, без промежуточного целочисленного продвижения. Преобразование не является сужающим, поэтому трёхстороннее сравнение корректно.
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/usual_arithmetic_conversions