命名空间
变体
操作

常用算术转换

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句 (循环)
for
range-for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (直到 C++17* 过时)
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
 
 

许多期望算术枚举类型操作数的二元运算符,以类似的方式引起转换并产生结果类型。目的是产生一个通用类型,这也是结果的类型。这种模式称为常用算术转换

目录

[编辑] 定义

常用算术转换定义如下

[编辑] 阶段 1

对两个操作数应用左值到右值转换,生成的纯右值将代替原始操作数用于剩余过程。

[编辑] 阶段 2

  • 如果任一操作数是作用域枚举类型,则不执行转换;如果另一个操作数没有相同的类型,则表达式是非良构的。
  • 否则,进行到下一阶段。
(自 C++11 起)

[编辑] 阶段 3

  • 如果任一操作数是枚举类型,而另一个操作数是不同的枚举类型或浮点类型,则表达式是非良构的。
  • 否则,进行到下一阶段。
(自 C++26 起)

[编辑] 阶段 4

  • 如果任一操作数是浮点类型,则应用以下规则
  • 如果两个操作数具有相同的类型,则不执行进一步的转换。
  • 否则,如果其中一个操作数是非浮点类型,则将该操作数转换为另一个操作数的类型。
  • 否则,如果操作数类型的浮点转换等级已排序但(自 C++23 起)不相等,则将浮点转换等级较低的类型的操作数转换为另一个操作数的类型。
  • 否则,如果操作数类型的浮点转换等级相等,则将浮点转换子等级较低的操作数转换为另一个操作数的类型。
  • 否则,表达式是非良构的。
(自 C++23 起)
  • 否则,两个操作数都是整数类型,进行到下一阶段。

[编辑] 阶段 5

两个操作数都转换为通用类型 C。给定类型 T1T2 作为操作数的提升类型(在整型提升规则下),应用以下规则来确定 C

  • 如果 T1T2 是相同的类型,则 C 是该类型。
  • 否则,如果 T1T2 都是有符号整数类型或都是无符号整数类型,则 C 是具有更大整数转换等级的类型。
  • 否则,T1T2 之间的一个类型是有符号整数类型 S,另一个类型是无符号整数类型 U。应用以下规则
  • 如果 U 的整数转换等级大于或等于 S 的整数转换等级,则 CU
  • 否则,如果 S 可以表示 U 的所有值,则 CS
  • 否则,C 是与 S 对应的无符号整数类型。

如果一个操作数是枚举类型,而另一个操作数是不同的枚举类型或浮点类型,则此行为已被弃用。

(自 C++20 起)
(直到 C++26)

[编辑] 整数转换等级

每个整数类型都有一个整数转换等级,定义如下

  • 除了 charsigned 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 charunsigned char 的等级。
  • char8_t 的等级等于 unsigned char 的等级。
(自 C++20 起)
(自 C++11 起)
  • wchar_t 的等级等于其实例定义的底层类型的等级。
  • 任何扩展有符号整数类型相对于另一个具有相同宽度的扩展有符号整数类型的等级是实现定义的,但仍受其他用于确定整数转换等级的规则的约束。
(自 C++11 起)
  • 对于所有整数类型 T1T2T3,如果 T1 的等级大于 T2,并且 T2 的等级大于 T3,则 T1 的等级大于 T3

整数转换等级也用于整型提升的定义中。

[编辑] 浮点转换等级和子等级

[编辑] 浮点转换等级

每个浮点类型都有一个浮点转换等级,定义如下

  • 标准浮点类型的等级按顺序递减
    • long double
    • double
    • float
  • 浮点类型 T 的等级大于任何浮点类型的等级,后者的值集是 T 的值集的真子集。
  • 具有相同值集的两个扩展浮点类型具有相等的等级。
  • 与恰好一个 cv-非限定标准浮点类型具有相同值集的扩展浮点类型的等级等于该标准浮点类型的等级。
  • 与多个 cv-非限定标准浮点类型具有相同值集的扩展浮点类型的等级等于 double 的等级。
(自 C++23 起)


浮点转换子等级

具有相等浮点转换等级的浮点类型按浮点转换子等级排序。子等级在具有相等等级的类型之间形成全序关系。

类型 std::float16_tstd::float32_tstd::float64_tstd::float128_t (固定宽度浮点类型) 具有比任何具有相等转换等级的标准浮点类型更大的转换子等级。否则,转换子等级顺序是实现定义的。

(自 C++23 起)

[编辑] 用法

浮点转换等级和子等级也用于

(自 C++23 起)

[编辑] 缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 应用于 已发布行为 正确行为
CWG 1642 C++98 常用算术转换可能涉及左值 首先应用左值到右值转换
CWG 2528 C++20 unsigned char 之间的三路比较
unsigned int 是非良构的,因为
中间的整型提升[1]
基于提升后的类型确定通用类型
无需实际提升操作数[2]
实际提升操作数[2]
CWG 2892 C++98 当两个操作数都是相同的
浮点类型时,“不需要进一步转换”的含义不明确
浮点类型时,“不需要进一步转换”的含义不明确
更改为“不执行进一步的
转换”
  1. 在解析之前,unsigned char 在阶段 5 的开始被提升为 int,然后转换为 unsigned int。但是,后一种转换是窄化转换,这使得三路比较是非良构的。
  2. 在解析之后,通用类型仍然是 unsigned int。不同之处在于,unsigned char 直接转换为 unsigned int,而没有中间的整型提升。转换不是窄化转换,因此三路比较是良构的。