移位导致奇怪的类型转换
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 都抱怨存在从 int
到 uint16_t
的隐式转换,但我不明白为什么引入位移会导致右侧表达式突然求值为int
.
编辑:对于 clang,我使用 -std=c++14 -Weverything
标志进行编译;对于 gcc,我使用 -std=c++14 -Wall -Wconversion
标志编译。
在使用整数类型进行任何算术运算之前,始终至少要提升到(有时,但在本例中不使用 gcc,unsigned
)int
。正如您在 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);
a
和1
升级为int
:int(a)
和int(byte(1))
.
a
向左移动一位:int result = int(a) << int(byte(1))
(结果是 int
)。
result
存储在 b
中。由于 int
比 byte
宽,将发出警告。
如果操作数是常量表达式,编译器可能能够在编译时计算结果并在结果不适合目标时发出警告:
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
以下代码在没有警告的情况下编译:
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 都抱怨存在从 int
到 uint16_t
的隐式转换,但我不明白为什么引入位移会导致右侧表达式突然求值为int
.
编辑:对于 clang,我使用 -std=c++14 -Weverything
标志进行编译;对于 gcc,我使用 -std=c++14 -Wall -Wconversion
标志编译。
在使用整数类型进行任何算术运算之前,始终至少要提升到(有时,但在本例中不使用 gcc,unsigned
)int
。正如您在 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);
a
和1
升级为int
:int(a)
和int(byte(1))
.a
向左移动一位:int result = int(a) << int(byte(1))
(结果是int
)。result
存储在b
中。由于int
比byte
宽,将发出警告。
如果操作数是常量表达式,编译器可能能够在编译时计算结果并在结果不适合目标时发出警告:
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