2016-11-21 84 views
2

tl; dr位操作是否安全,并且在整数提升时的预期行为(类型短于int)?使用整数提升的位操作

例如

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)转换(((无符号 字符)((int)的x1.X ::值))|((无符号字符)((int)的x2.X ::值)))”从 'INT' 为 'X ::∪{又名无符号字符}' 内{} [-Wnarrowing]

所以我添加一个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)}; 
} 

问题:能否整数提升,然后narro翼投与预期结果(*)混乱?特别是因为这些是演员改变签名的前后(unsigned char - >int - >unsigned char)。如果U已签名,那么怎么办?std::int8_t(它不会在我的代码中签名,但会对此行为感到好奇)。

我常见的sens说代码完全正常,但是我的C++偏执狂说至少有一个实现定义行为的机会。

(*)是它的不明确的情况下(或I弄乱)的预期行为是设置或清除位(x1是值,x2是掩模,B是设置/清除操作)

回答

2

如果您使用无符号类型,则全部都可以。

4.7积分转化[conv.integral]
...
2如果目标类型为无符号的,所得到的值是最小的。对于无符号整数目标类型,变窄是完全定义的标准任务无符号整数与源一致 integer(模2n,其中n是用于表示无符号类型的位数)。

但是,如果目标类型有符号,结果实现定义,每下一段落(强调雷):

3如果目的地是带符号的数值不变,如果它可以用目标类型表示; 否则,值为实现定义的

在常见的实现一切都会好的,因为它是唯一保持低水平字节无符号或符号类型编译器简单根本就缩小转换。但该标准只要求执行定义会发生什么。当原始值不能用目标类型表示时,实现可以记录将值缩小为带符号类型的值给出0,并且仍然是符合的。


顺便说一句,作为C++和C经常工艺转换以同样的方式,但应注意的是,C标准稍有不同,因为最后一种情况下可以提高信号:

6.3.1.3 [转换]有符号和无符号整数
...
3否则,新类型有符号且值不能在其中表示; 结果是实现定义的或者实现定义的信号被提升

仍然是一个确认,C和C++是不同的语言......

+0

(签订目标):这些位操作的情况下,我认为任何结果可以表示回目标类型,从而避免实现定义的行为。我对么? – bolov

+0

在所有*正常*架构中,你是对的。我想知道什么可能会破坏那个('int8_t'是两个补码,但是普通的'int'不是),并且如果这可以按照标准被允许的话。现在我没有明确的答案... –

+1

@bolov:我已经深入了解标准,并且没有任何东西禁止一个实现支持'int8_t'类型,但是使用*符号和幅度*来获得普通的'int'。在这种情况下'((int8_t)-128)| (int8_t)1'可以给'-129'应用积分促销(转换为int)。因此,没有什么能保证按位操作对签名类型是稳定的(导致与操作数类型相同)。 –