首页 > 解决方案 > 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_tunsigned int

标签: c++language-lawyerinteger-promotion

解决方案


什么是“通常的算术转换”,它们在标准中的定义是什么?

[expr.arith.conv]/1

许多期望算术或枚举类型的操作数的二元运算符会以类似的方式导致转换和产生结果类型。目的是产生一个通用类型,这也是结果的类型。这种模式称为通常的算术转换,其定义如下:

  • (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_tvs int(foroperator+operator!=以后),#1.5被应用,uint8_t将被提升为int,结果operator+也是int

另一方面,for unsigned intvs int(for operator+),应用#1.5.3,int将转换为unsigned int,结果operator+unsigned int


推荐阅读