位移导致奇怪的类型转换
Bit shift leads to strange type conversion
以下代码编译时不发出警告:
std::uint16_t a = 12;
std::uint16_t b = a & 0x003f;
但是,与位位一起执行位移位并导致"隐式强制转换警告":
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
标志编译。
来自 cppreference.com:">如果传递给算术运算符的操作数是整型或无作用域枚举类型,则在任何其他操作之前(但在左值到右值转换之后,如果适用(,操作数将进行积分提升。
例如:
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
对整数类型进行任何算术运算之前总是至少提升到(有时,但在这种情况下不使用 gcc,unsigned
(int
。正如您从此示例中看到的,这首先也适用于您,无警告变体。
绕过这些(诚然经常令人惊讶(整数促销规则的最佳方法可能是从一开始就使用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。
但我不明白为什么引入位移会导致右手表达式突然计算为 int。
我认为你误解了警告。在这两种情况下,表达式的计算结果都int
但在第一种情况下,结果总是适合uint16_t
,在第二种情况下则不适合。看起来编译器足够聪明,可以检测到这一点,并且仅在第二种情况下生成警告。
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- C++中的双指针类型转换
- 逐位操作的隐式类型转换
- 模板中的类型转换
- 在 C++(和 C)中进行类型转换时明显不一致
- 字符类型转换不兼容
- 将复杂的非基元C++数据类型转换为 Erlang/Elixir 格式,以使用 NIF 导出方法
- C++:用户定义的显式类型转换函数错误
- 将类指针类型转换为键时出错
- 通过引用传递参数时C++类型转换
- 在 C++ 中将一个模板类型的对象类型转换为另一个模板类型
- C++显式类型转换(C 样式强制转换)的强制表示法和static_cast的多种解释
- C++无效的函数类型转换
- 在将派生类指针类型转换为派生类指针后,从基类指针调用派生类函数
- 如何将Windows产品类型转换为名称?
- 通过构造函数方法输出的类到类类型转换是 5500 为什么不是 5555
- 事件系统:使用类型转换或联合进行继承
- 如何在参数中定义隐式类型转换的构造函数?
- 类模板实例化中的类型转换