位移导致奇怪的类型转换

Bit shift leads to strange type conversion

本文关键字:类型转换      更新时间:2023-10-16

以下代码编译时不发出警告:

std::uint16_t a = 12;
std::uint16_t b = a & 0x003f;

但是,与位位一起执行位移位并导致"隐式强制转换警告":

std::uint16_t b = (a & 0x003f) << 10; // Warning generated.

gcc 和 clang 都抱怨存在从 intuint16_t 的隐式转换,但我不明白为什么引入位移会导致右手表达式突然评估为int

编辑:对于clang,我用-std=c++14 -Weverything标志编译;对于gcc,我用-std=c++14 -Wall -Wconversion标志编译。

来自 cppreference.com:">如果传递给算术运算符的操作数是整型或无作用域枚举类型,则在任何其他操作之前(但在左值到右值转换之后,如果适用(,操作数将进行积分提升

例如:

byte a = 1;
byte b = a << byte(1);
  1. a1晋升为intint(a)int(byte(1))
  2. a向左移动一个位置:int result = int(a) << int(byte(1))(结果是int(。
  3. result存储在b 中。由于intbyte宽,因此将发出警告。

如果操作数是常量表达式,编译器可能能够在编译时计算结果,并在结果不适合目标时发出警告:

byte b = 1 << 1; // no warning: does not exceed 8 bits
byte b = 1 << 8; // warning: exceeds 8 bits

或者,使用 constexpr

constexpr byte a = 1;
byte b = a << 1; // no warning: it fits in 8 bits
byte b = a << 8; // warning: it does not fit in 8 bits

对整数类型进行任何算术运算之前总是至少提升到(有时,但在这种情况下不使用 gcc,unsigned (int。正如您从此示例中看到的,这首先也适用于您,无警告变体。

绕过这些(诚然经常令人惊讶(整数促销规则的最佳方法可能是从一开始就使用unsigned int(或常见平台上的uint32_t(。

如果您不能或不想使用较大的类型,则可以将整个表达式的结果static_caststd::uint16_t

std::uint16_t b = static_cast<std::uint16_t>((a & 0x003f) << 10); 

这将正确生成 RHS 值 mod 2^16。

但我不明白为什么引入位移会导致右手表达式突然计算为 int。

我认为你误解了警告。在这两种情况下,表达式的计算结果都int但在第一种情况下,结果总是适合uint16_t,在第二种情况下则不适合。看起来编译器足够聪明,可以检测到这一点,并且仅在第二种情况下生成警告。