C ++意外整数提升

时间:2019-05-10 00:43:10

标签: c++ language-lawyer integer-promotion

我最近正在编写一些本应用于测试其他代码的代码,但我偶然发现了一个令人惊讶的整数提升案例。这是最小的测试用例:

#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;如果指定的关系为false,则产生false。   错误。

什么是“通常的算术转换”?在标准中它们在哪里定义?我的猜测是,对于某些运算符,它们隐式地将较小的整数提升为intunsigned int(这也得到以下事实的支持:用std::uint8_t替换unsigned int会得到0,并且进一步因为赋值运算符缺少“通常的算术转换”子句。

2 个答案:

答案 0 :(得分:2)

  

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

[expr.arith.conv]/1

  

许多期望算术或   枚举类型原因转换和收益结果类型类似   办法。目的是产生一个通用类型,这也是   结果。这种模式称为通常的算术转换,   定义如下:

     
      
  • (1.1)如果操作数中的任何一个为scoped enumeration type,则不进行任何转换   执行;如果另一个操作数的类型不同,则   表达式格式错误。

  •   
  • (1.2)如果一个操作数的类型为long double,则另一个应为   转换为长双。

  •   
  • (1.3)否则,如果任一操作数为double,则另一个应为   转换为两倍。

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

另一方面,对于unsigned intint(对于operator+),应用#1.5.3,int将转换为unsigned int ,而operator+的结果为unsigned int

答案 1 :(得分:2)

您的猜测是正确的。 C ++中许多运算符的操作数(例如,二进制算术和比较运算符)都需要进行通常的算术转换。在C ++ 17中,通常的算术转换在[expr]/11中指定。我不会在这里引用整个段落,因为它很大(您可以单击链接),但是对于整数类型,通常的算术转换归结为应用了整数提升,然后在某种意义上有效地进行了一些提升如果在初始整数提升之后两个操作数的类型不同,则较小的类型将转换为两个中较大的一个。整数提升基本上意味着小于int的任何类型都将提升为intunsigned int,二者中的任何一个都可以代表原始类型的所有可能值,主要是是导致您的示例出现此问题的原因。

您已经弄清楚自己在代码中,通常的算术转换发生在a = a + 1;中,最明显的是在if条件下

if (a != b + 1)
    …

它们将b提升为int的位置,从而使b + 1的结果为int类型,并且a被提升到int!=,因此发生在类型int的值上,这导致条件为true而不是false…