问题 两个无符号的减法给出了签名


我有以下代码:

#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),还是这种奇怪的编译器bug(用GCC和clang测试过)?


11432
2017-10-05 23:40


起源

这似乎 相应 (促销在C和C ++中至少相似,如果不相同的话)。 - chris
是的,任何小于的类型 int 促进 int 算术。 - ShadowRanger
还要避免 reinterpret_cast,它是实现定义的。 - vsoftco


答案:


是的,这是预期的,作为所谓的一部分 通常的算术转换 结合规则 整体推广

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


[C++03: 4.5/1]: 类型的右值 charsigned charunsigned charshort int,或未签名 short int 可以转换为int类型的右值 int 可以表示源类型的所有值;否则,源rvalue可以转换为类型的右值 unsigned int

[C++03: 4.5/5]: 这些转换被称为 整体促销

[C++03: 5/9]: 许多期望算术或枚举类型的操作数的二元运算符会以类似的方式引起转换并产生结果类型。目的是产生一个通用类型,它也是结果的类型。

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

  • 如果任一操作数是类型 long double,另一个应转换为 long double
  • 否则,如果任一操作数是 double,另一个应转换为双倍。
  • 否则,如果任一操作数是 float,另一个应转换为 float
  • 否则,应对两个操作数执行整体促销(4.5)。54
  • 然后,如果任一操作数是 unsigned long 另一个应转换为 unsigned long
  • 否则,如果一个操作数是a long int 和另一个 unsigned int,如果一个 long int 可以表示unsigned int的所有值,unsigned int应该转换为a long int;否则两个操作数都应转换为无符号 long int
  • 否则,如果任一操作数是 long,另一个应转换为 long
  • 否则,如果任一操作数是 unsigned,另一个应转换为 unsigned

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


[C++11: 4.5/1]: 除了的整数类型的prvalue boolchar16_tchar32_t, 要么 wchar_t 其整数转换等级(4.13)小于等级 int 可以转换为类型的prvalue int 如果 int 可以表示源类型的所有值;否则,源prvalue可以转换为unsigned类型的prvalue int

[C++11: 4.5/7]: 这些转换被称为 整体促销

[C++11: 5.9]: 许多期望算术或枚举类型的操作数的二元运算符会以类似的方式引起转换并产生结果类型。目的是产生一个通用类型,它也是结果的类型。

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

  • 如果任一操作数具有作用域枚举类型(7.2),则不执行任何转换;如果另一个操作数的类型不同,则表达式格式不正确。
  • 如果任一操作数的类型为long double,则另一个操作数应转换为long double。
  • 否则,如果任一操作数为double,则另一个操作数应转换为double。
  • 否则,如果任一操作数是浮点数,则另一个操作数应转换为浮点数。
  • 否则,应对两个操作数执行整体促销(4.5)。59然后,以下规则应适用于提升的操作数:      
    • 如果两个操作数具有相同的类型,则不需要进一步转换。
    • 否则,如果两个操作数都具有有符号整数类型或两者都具有无符号整数类型,则具有较小整数转换等级类型的操作数应转换为具有较大等级的操作数的类型。
    • 否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,则带有符号整数类型的操作数应转换为具有无符号整数类型的操作数的类型。
    • 否则,如果带有符号整数类型的操作数的类型可以表示具有无符号整数类型的操作数类型的所有值,则具有无符号整数类型的操作数应转换为带有符号整数类型的操作数的类型。
    • 否则,两个操作数都应转换为与带符号整数类型的操作数类型相对应的无符号整数类型。

11
2017-10-05 23:45



哦,等一下......在这两种情况下,这段代码的整体促销活动......噢呃 - Lightness Races in Orbit
@LightnessRacesinOrbit,我只是在寻找并想知道这是不是发生了什么。惭愧,因为这是令人兴奋的学习。 - chris
修复;我错了:P - Lightness Races in Orbit
这真的很好知道。这值得一个更大,更有针对性的自我回答的问题,更容易找到,Lightness Races in Orbit。埋在这里,这个答案可能会被忽视。 - user4581301
@LightnessRacesinOrbit然后,由于该规则[C ++ 11:4.5 / 1],在进行减法时,两个uint8_t都被提升为整数。不知道会发生这种情况:/ - Noxbru