C ++意外整数提升

cod*_*ith 7 c++ integer-promotion language-lawyer

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

令人惊讶的是,该程序返回1。一些调试和预感表明,b + 1在条件a + 1语句中实际上返回了256,而在赋值操作中产生了预期值0。

C ++ 17草案的第8.10.6节(关于等式/不等号运算符)指出:

如果两个操作数均为算术或枚举类型,则对两个操作数执行常规的算术转换;如果指定的关系为真,则每个运算符都将产生true;如果指定的关系为false,则每个运算符应产生false。

什么是“通常的算术转换”,在标准中它们在哪里定义?我的猜测是,它们隐式地将较小的整数提升为某些运算符intunsigned int为某些运算符提升(这也得到以下事实的支持:用产生的结果替换std::uint8_tunsigned int0,而且赋值运算符缺少“常规算术转换”子句)。

son*_*yao 2

\n

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

\n
\n\n

[expr.arith.conv]/1

\n\n
\n

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

\n\n
    \n
  • (1.1) 如果任一操作数是作用域枚举类型,则不执行任何转换;如果另一个操作数没有相同的类型,则表达式的格式错误。

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

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

  • \n
  • (1.4) 否则,如果其中一个操作数是浮点型,则另一个操作数应转换为浮点型。

  • \n
  • (1.5) 否则,将对两个操作数执行积分提升 ( [conv.prom] )。59那么以下规则应适用于提升的操作数:

    \n\n
      \n
    • (1.5.1) 如果两个操作数具有相同的类型,则不需要进一步转换。

    • \n
    • (1.5.2) 否则,如果两个操作数都具有有符号整数类型或两个操作数都具有无符号整数类型,则具有较小整数转换等级的类型的操作数应转换为具有较大等级的操作数的类型。

    • \n
    • (1.5.3) 否则,如果具有无符号整数类型的操作数的等级大于或等于另一个操作数的类型的等级,则具有有符号整数类型的操作数应转换为该类型无符号整数类型的操作数。

    • \n
    • (1.5.4) 否则,如果有符号整型操作数的类型可以表示无符号整型操作数类型的所有值,则无符号整型操作数应转换为有符号整数类型的操作数类型。

    • \n
    • (1.5.5) 否则,两个操作数都应转换为与有符号整数类型操作数的类型相对应的无符号整数类型。

    • \n
  • \n
\n\n

59)因此,bool、char8_\xc2\xadt、char16_\xc2\xadt、char32_\xc2\xadt、wchar_\xc2\xadt 或枚举类型的操作数将转换为某种整型。

\n
\n\n

对于uint8_tvs int(对于operator+operator!=以后),应用#1.5,uint8_t将提升为int,并且 的结果operator+也是int

\n\n

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

\n