Spec-Zone.ru › C++

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

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

Определение

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

Этап 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 — знаковый), не имеют одинакового ранга, даже если у них одинаковое представление.
  • Ранг знакового целочисленного типа больше ранга любого знакового целочисленного типа с меньшей шириной.
  • Ранги следующих целочисленных типов уменьшаются в указанном порядке:
  • long long
(с 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.
  • Ранг char8_t равен рангу unsigned char.
(с 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.

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

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

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

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

  • Ранги стандартных типов с плавающей точкой уменьшаются в указанном порядке:
    • long double
    • double
    • float
  • Ранг типа с плавающей точкой T больше ранга любого типа с плавающей точкой, множество значений которого является надмножеством множества значений T.
  • Два расширенных типа с плавающей точкой с одинаковым множеством значений имеют равные ранги.
  • Расширенный тип с плавающей точкой с тем же набором значений, что и ровно один неквалифицированный стандартный тип с плавающей точкой, имеет ранг, равный рангу этого стандартного типа с плавающей точкой.
  • Расширенный тип с плавающей точкой с тем же набором значений, что и более чем у одного неквалифицированного стандартного типа с плавающей точкой, имеет ранг, равный рангу 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 обычные арифметические преобразования могут включать lvalues сначала применяются преобразования lvalue в rvalue
CWG 2528 C++20 трёхстороннее сравнение между unsigned char
и unsigned int некорректно, поскольку
происходит промежуточное целочисленное продвижение[1]
определяет общий тип на основе
продвинутых типов, без
фактического продвижения операндов[2]
  1. До решения, unsigned char продвинулся до int в начале стадии 5, а затем преобразуется в unsigned int. Однако последнее преобразование является сужающим, что делает трёхстороннее сравнение некорректным.
  2. После решения, общий тип всё ещё 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

Spec-Zone.ru

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