具有整数提升的位操作

时间:2016-11-21 13:47:48

标签: c++ bit-manipulation integer-promotion

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)

1 个答案:

答案 0 :(得分:2)

如果使用无符号类型,则一切正常。标准要求对于无符号目标整数类型,缩小是完美定义的:

  

4.7整数转换[conv.integral]   
...
  2如果目标类型是无符号的,则结果值是与源一致的最小无符号整数   整数(模2n,其中n是用于表示无符号类型的位数)。

但如果目标类型已签名,则结果为实现定义,按照下一段(强调我的):

  

3如果目标类型已签名,则该值如果可以在目标类型中表示,则不会更改;   否则,该值为实现定义

在通常的实现中,一切都会好的,因为编译器只需要为无符号或有符号类型保留低级别字节,就可以简单地缩小转换范围。但是标准只要求实现定义会发生什么。当原始值无法在目标类型中表示给出0时,实现可以记录将值缩小为有符号类型,并且仍然符合要求。

顺便说一下,由于C ++和C经常以相同的方式处理转换,因此应该注意C标准略有不同,因为最后一种情况可能会引发信号:

  

6.3.1.3 [转换]有符号和无符号整数   
... ... 3否则,新类型已签名,其值无法表示;无论是   结果是实现定义的或引发实现定义的信号

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

相关问题