两个无符号数相减得到有符号数的解释

13
我有以下代码片段:

code:

#include <cstdint>

template <typename T>
T test(T a, T b)
{
    float aabb = reinterpret_cast<float>(a - b);
}

int main(int argc, const char *argv[])
{
    std::uint8_t a8, b8;
    test(a8, b8);
    return 0;
}

我知道 reinterpret_cast<float> 无法工作并且会在编译时出现错误。我利用这个错误来让编译器告诉我 a - b 的类型。
问题在于,这种情况下,它说 a - b 的类型为 int,当它们两个都是 uint8_t (unsigned char) 时也是如此。对于 uint16_t 也是一样的。但是对于 uint32_t,它会说 a - b 的类型是 unsigned int
所以,我的问题是: 这是预期行为(即 unsigned char - unsigned char 得到 int),还是某种奇怪的编译器错误(已测试使用 GCC 和 clang)?

3
这似乎很相关(在C和C++中,类型提升至少是相似的,如果不是完全相同的,在加法运算中发生)。 - chris
1
是的,任何比 int 更小的类型在算术运算中都会提升为 int - ShadowRanger
同时避免使用reinterpret_cast,因为它的行为是由实现定义的。 - vsoftco
1个回答

12

是的,这是预期的结果,因为它符合所谓的常规算术转换整数提升规则。

C++03和C++11之间的确切措辞有所变化,但在这种情况下结果是相同的。


[C++03: 4.5/1]: 如果int可以表示源类型的所有值,则char、signed char、unsigned char、short int或unsigned short int类型的右值可以转换为int类型的右值;否则,源rvalue可以转换为unsigned int类型的rvalue。

[C++03: 4.5/5]: 这些转换被称为整数提升

[C++03: 5/9]: 许多希望算术或枚举类型的操作数的二元运算符会以类似的方式进行转换,并产生结果类型。其目的是生成一个共同的类型,这也是结果的类型。

这种模式被称为常规算术转换,定义如下:

  • 如果任一操作数的类型为long double,则另一个操作数必须转换为long double。
  • 否则,如果任一操作数为double,则另一个操作数必须转换为double。
  • 否则,如果任一操作数的类型为float,则另一个操作数必须转换为float。
  • 否则,必须对两个操作数执行整数提升 (4.5)。54
  • 然后,如果任一操作数的类型为unsigned long,则另一个操作数必须转换为unsigned long。
  • 如果一个操作数是long int类型,而另一个操作数是unsigned int类型,则如果long int类型可以表示所有的unsigned int类型的值,则将unsigned int转换为long int; 否则,两个操作数都将被转换为unsigned long int
  • 如果其中一个操作数是long类型,则另一个操作数将被转换为long类型。
  • 如果其中一个操作数是unsigned类型,则另一个操作数将被转换为unsigned类型。

[注:否则,唯一剩下的情况就是两个操作数都是int类型]

  • 如果任一操作数是枚举类型(7.2)的,则不执行转换,如果另一个操作数与之类型不同,则表达式不合法。
  • 如果任一操作数为long double类型,则另一个操作数应转换为long double类型。
  • 否则,如果任一操作数为double类型,则另一个操作数应转换为double类型。
  • 否则,如果任一操作数为float类型,则另一个操作数应转换为float类型。
  • 否则,应对两个操作数进行整型提升(4.5)。59 然后对所提升的操作数应用以下规则:
    • 如果两个操作数具有相同的类型,则不需要进一步的转换。
    • 否则,如果两个操作数都具有带符号整数类型或都具有无符号整数类型,则具有较低整数转换等级的操作数应转换为具有较高转换等级的操作数的类型。
    • 否则,如果具有无符号整数类型的操作数的等级大于或等于另一个操作数的类型的等级,则具有带符号整数类型的操作数应转换为具有无符号整数类型的操作数的类型。
    • 否则,如果具有带符号整数类型的操作数的类型可以表示另一个操作数的类型的所有值,则具有无符号整数类型的操作数应转换为具有带符号整数类型的操作数的类型。
    • 否则,两个操作数都应转换为与具有带符号整数类型的操作数的类型相对应的无符号整数类型。

  • 3
    哦等一下.. 在这段代码中都有整型提升... 哦噢 - Lightness Races in Orbit
    @LightnessRacesinOrbit,我只是在看,想知道那是否发生了。很遗憾,因为那是一个令人兴奋的学习机会。 - chris
    这真的很好知道。这值得一个更大、更好地针对自我回答的问题,这样更容易找到,Lightness Races in Orbit。埋在这里,这个答案很可能会被忽视。 - user4581301
    由于规则[C++11:4.5/1],在执行减法时,两个uint8_t都被提升为int。不知道会发生这种情况:/ - Noxbru

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接