移位导致奇怪的类型转换

Bit shift leads to strange type conversion

以下代码在没有警告的情况下编译:

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

但是,随着按位执行移位并导致 'implicit cast warning':

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 标志编译。

在使用整数类型进行任何算术运算之前,始终至少要提升到(有时,但在本例中不使用 gcc,unsignedint。正如您在 this example 中看到的那样,这首先适用于您,无警告变体也是如此。

绕过这些(诚然经常令人惊讶)整数提升规则的最好方法可能是从一开始就使用 unsigned int(或在通用平台上 uint32_t)。

如果你不能或不想使用更大的类型,你可以 static_cast 整个表达式的结果返回 std::uint16_t:

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

这将正确地产生 RHS 值 mod 2^16.

yet I fail to see why introducing the bit shift would cause the right hand expression to suddenly evaluate to an int.

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

来自 cppreference.com"If the operand passed to an arithmetic operator is integral or unscoped enumeration type, then before any other action (but after lvalue-to-rvalue conversion, if applicable), the operand undergoes integral promotion."

例如:

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