c++ - C++ 意外整数提升
问题描述
我最近正在编写一些实际上应该测试其他代码的代码,我偶然发现了一个令人惊讶的整数提升案例。这是最小的测试用例:
#include <cstdint>
#include <limits>
int main()
{
std::uint8_t a, b;
a = std::numeric_limits<std::uint8_t>::max();
b = a;
a = a + 1;
if (a != b + 1)
return 1;
else
return 0;
}
令人惊讶的是,这个程序返回 1。一些调试和预感显示,b + 1
在条件中实际上返回 256,而a + 1
在赋值中产生了预期值 0。
C++17 草案的第 8.10.6 节(关于相等/不相等运算符)指出
如果两个操作数都是算术或枚举类型,则对两个操作数执行通常的算术转换;如果指定的关系为 true,则每个运算符都应返回 true,如果为 false,则返回 false。
什么是“通常的算术转换”,它们在标准中的定义是什么?我的猜测是,它们隐含地将较小的整数提升到某些运算符int
或为某些运算符(这也得到了替换为0unsigned int
的事实的支持,并且赋值运算符缺少“通常的算术转换”子句)。std::uint8_t
unsigned int
解决方案
什么是“通常的算术转换”,它们在标准中的定义是什么?
许多期望算术或枚举类型的操作数的二元运算符会以类似的方式导致转换和产生结果类型。目的是产生一个通用类型,这也是结果的类型。这种模式称为通常的算术转换,其定义如下:
(1.1) 如果任一操作数是作用域枚举类型,则不执行任何转换;如果另一个操作数的类型不同,则表达式格式错误。
(1.2) 如果任一操作数是 long double 类型,则另一个应转换为 long double。
(1.3) 否则,如果任一操作数为双精度,则另一个应转换为双精度。
(1.4) 否则,如果任一操作数为浮点数,则另一个应转换为浮点数。
(1.5) 否则,应在两个操作数上执行积分提升 ( [conv.prom] )。59然后以下规则应适用于提升的操作数:
(1.5.1) 如果两个操作数的类型相同,则不需要进一步转换。
(1.5.2) 否则,如果两个操作数都具有有符号整数类型或都具有无符号整数类型,则具有较小整数转换等级的类型的操作数应转换为具有较大等级的操作数的类型。
(1.5.3) 否则,如果无符号整数类型的操作数的秩大于或等于另一个操作数类型的秩,则将有符号整数类型的操作数转换为无符号整数类型的操作数的类型类型。
(1.5.4) 否则,如果有符号整数类型的操作数的类型可以表示无符号整数类型的操作数类型的所有值,则无符号整数类型的操作数应转换为有符号整数类型的操作数类型有符号整数类型。
(1.5.5) 否则,两个操作数都应转换为无符号整数类型,对应于带符号整数类型的操作数的类型。
59)结果,bool、char8_t、char16_t、char32_t、wchar_t 或枚举类型的操作数被转换为某种整数类型。
对于uint8_t
vs int
(foroperator+
及operator!=
以后),#1.5被应用,uint8_t
将被提升为int
,结果operator+
也是int
。
另一方面,for unsigned int
vs int
(for operator+
),应用#1.5.3,int
将转换为unsigned int
,结果operator+
为unsigned int
。
推荐阅读
- javascript - 在 Elastic Beanstalk 中运行 Docker 构建脚本之前将文件复制到构建目录中?
- angular - 使用 Angular 中的反应形式修补数组内部的数组
- sql - 聚合后的窗口函数
- python - Python、selenium 机器人下拉选择
- angularjs - 当我使用 $locationProvider.html5Mode(true); 时,$window.location.href 刷新整个页面;
- sql - 如何使用匹配值使用 CSV 文件中的数据更新 Postgres 中的列
- anaconda - Anaconda 安装错误
- javascript - 有没有办法将数据流式传输到一个 blob(或生成一个巨大的 blob)
- ios - 使用键从 2 个 JSON 对象创建单个 JSON 字符串
- windows - SSMS 17.5当单元格数据大于可用显示区域时不显示椭圆