Spec-Zone.ru › C++

Обычные арифметические преобразования

Многие бинарные операторы, ожидающие операнды арифметического типа (arithmetic) или перечислимого типа, выполняют преобразования и формируют результирующие типы сходным образом. Цель состоит в том, чтобы получить общий тип, который также является типом результата. Этот шаблон называется обычными арифметическими преобразованиями.

Определение

Обычные арифметические преобразования определяются следующим образом:

Этап 1

К обоим операндам применяется преобразование из lvalue в rvalue; полученные prvalue используются вместо исходных операндов на оставшихся этапах.

Этап 2

  • Если хотя бы один операнд имеет тип scoped enumeration, преобразования не выполняются; если другой операнд имеет отличный тип, выражение некорректно.
  • В противном случае перейдите к следующему этапу.
(начиная с 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 является знаковым), не имеют одинаковый ранг, даже если их представления совпадают.
  • Ранг знакового целочисленного типа выше ранга любого знакового целочисленного типа меньшей ширины.
  • Ранги следующих целочисленных типов убывают в указанном порядке:
  • longlong
(начиная с 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.
  • Ранг char8_t равен рангу unsignedchar.
(начиная с C++20)
  • Ранг char16_t равен рангу std::uint_least16_t.
  • Ранг char32_t равен рангу std::uint_least32_t.
(начиная с C++11)
  • Ранг wchar_t равен рангу его базового типа, определяемого реализацией.
  • Ранг любого расширенного знакового целочисленного типа относительно другого расширенного знакового целочисленного типа той же ширины определяется реализацией, но при этом должен соответствовать остальным правилам определения ранга целочисленного преобразования.
(начиная с C++11)
  • Для всех целочисленных типов T1, T2 и T3, если ранг T1 выше ранга T2, а ранг T2 выше ранга T3, то ранг T1 выше ранга T3.

Ранг целочисленного преобразования также используется в определении целочисленного продвижения.

Ранг и подранг преобразования типов с плавающей точкой

Ранг преобразования типов с плавающей точкой

Для каждого типа с плавающей точкой определён ранг преобразования типов с плавающей точкой следующим образом:

  • Ранги стандартных типов с плавающей точкой убывают в указанном порядке:
    • longdouble
    • double
    • float
  • Ранг типа с плавающей точкой T выше ранга любого типа с плавающей точкой, множество значений которого является собственным подмножеством множества значений T.
  • Два расширенных типа с плавающей точкой с одинаковыми множествами значений имеют равные ранги.
  • Расширенный тип с плавающей точкой, имеющий то же множество значений, что и ровно один стандартный тип с плавающей точкой без cv-квалификаторов, имеет ранг, равный рангу этого стандартного типа с плавающей точкой.
  • Расширенный тип с плавающей точкой, имеющий то же множество значений, что и несколько стандартных типов с плавающей точкой без cv-квалификаторов, имеет ранг, равный рангу double.
(начиная с C++23)

Подранг преобразования типов с плавающей точкой

Типы с плавающей точкой, имеющие одинаковые ранги преобразования, упорядочиваются по подрангу преобразования типов с плавающей точкой. Подранг задаёт полный порядок среди типов с одинаковыми рангами.

Типы std::float16_t, std::float32_t, std::float64_t и std::float128_t (типы с плавающей точкой фиксированной ширины) имеют подранг преобразования выше, чем любой стандартный тип с плавающей точкой с равным рангом преобразования. В остальных случаях порядок подрангов преобразования определяется реализацией.

(начиная с C++23)

Использование

Ранг и подранг преобразования типов с плавающей точкой также используются для следующих целей:

  • определения того, может ли преобразование между различными типами с плавающей точкой быть неявным или является ли оно сужающим преобразованием,
  • различения последовательностей преобразований при разрешении перегрузки,
  • определения фактического типа, извлекаемого функцией std::num_get::get() при извлечении расширенного типа с плавающей точкой с помощью std::basic_istream::operator>>,
  • определения фактического типа, вставляемого функцией std::num_put::put() при вставке расширенного типа с плавающей точкой с помощью std::basic_ostream::operator<<,
(начиная с 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 когда оба операнда имеют один и тот же
тип с плавающей точкой, значение выражения «дальнейшее
преобразование не требуется» было неясно
заменено на «дальнейшее
преобразование не будет выполняться»
  1. До принятия решения unsignedchar продвигался до int в начале этапа 5, а затем преобразовывался в unsignedint. Однако последнее преобразование является сужающим, что делает трёхстороннее сравнение некорректным.
  2. После принятия решения общим типом по-прежнему является 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API