tl; dr当进行整数提升(类型短于int
)时,位操作是否安全并且表现如预期?
e.g。
uint8_t a, b, c;
a = b & ~c;
这是我所拥有的粗略的MCVE:
struct X { // this is actually templated
using U = unsigned; // U is actually a dependent name and can change
U value;
};
template <bool B> auto foo(X x1, X x2) -> X
{
if (B)
return {x1.value | x2.value};
else
return {x1.value & ~x2.value};
}
这很有效,但当U
更改为比int
更短的整数类型时,例如std::uint8_t
然后由于整数提升,我收到警告:
警告:缩小'(int)的转换(((无符号 char)((int)x1.X :: value))| ((unsigned char)((int)x2.X :: value)))'from 在{} [-Wnarrowing]内的'int'到'X :: U {aka unsigned char}'
所以我添加了static_cast
:
struct X {
using U = std::uint8_t;
U value;
};
template <bool B> auto foo(X x1, X x2) -> X
{
if (B)
return {static_cast<X::U>(x1.value | x2.value)};
else
return {static_cast<X::U>(x1.value & ~x2.value)};
}
问题:整数提升然后缩小演员是否会与预期结果混淆(*)?特别是因为这些是强制转换后退和转发签名(unsigned char
- &gt; int
- &gt; unsigned char
)。如果U
已签名,即std::int8_t
(我的代码中不会签名,但会对此行为感到好奇)。
我常见的感觉说代码完全没问题,但我的C ++偏执狂说至少有可能实现定义的行为。
(*)是不清楚的(或者我搞砸了)预期的行为是设置或清除位(x1
是值,x2
是掩码,{{1}是设置/清除op)
答案 0 :(得分:2)
如果使用无符号类型,则一切正常。标准要求对于无符号目标整数类型,缩小是完美定义的:
4.7整数转换[conv.integral]
...
2如果目标类型是无符号的,则结果值是与源一致的最小无符号整数 整数(模2n,其中n是用于表示无符号类型的位数)。
但如果目标类型已签名,则结果为实现定义,按照下一段(强调我的):
3如果目标类型已签名,则该值如果可以在目标类型中表示,则不会更改; 否则,该值为实现定义。
在通常的实现中,一切都会好的,因为编译器只需要为无符号或有符号类型保留低级别字节,就可以简单地缩小转换范围。但是标准只要求实现定义会发生什么。当原始值无法在目标类型中表示给出0
时,实现可以记录将值缩小为有符号类型,并且仍然符合要求。
顺便说一下,由于C ++和C经常以相同的方式处理转换,因此应该注意C标准略有不同,因为最后一种情况可能会引发信号:
6.3.1.3 [转换]有符号和无符号整数
... ... 3否则,新类型已签名,其值无法表示;无论是 结果是实现定义的或引发实现定义的信号。
仍然确认C和C ++是不同的语言......