定义良好的位偏移超过32位,没有编译器警告

Well-defined bit-shifting more than 32 bits w/o compiler warning

本文关键字:32位 警告 编译器 定义      更新时间:2023-10-16

这是一个愚蠢的问题,部分原因是为了好玩。

我有一个"定义明确"(或"饱和"?(的位掩码函数

template <unsigned N>
uint32_t mask(uint32_t x) {
  const uint32_t MASK = N >= 32 ? ~uint32_t(0) : (uint32_t(1) << N) - 1;
  return x & MASK;
}

预期行为:

uint32_t x = ~uint32_t(0); // 0xFFFFFFFF
mask<8>(x) => 0x000000FF
mask<24>(x) => 0x00FFFFFF
mask<32>(x) => 0xFFFFFFFF
mask<1234>(x) => 0xFFFFFFFF

但我不喜欢在mask<1234>()中有一个未定义的代码uint32_t(1) << 1234,尽管它是100%无害的(不应该评估它(。我不想看到编译器警告。

  • 为什么不';t左位移位<lt&";,对于32位整数,当使用超过32次时,是否能按预期工作 请参阅下面的更新

请给我一些小技巧(和模板元编程?(来摆脱uint32_t(1) << 1234

我有GCC 4.9,它(部分(支持C++14,并且足够聪明,可以进行恒定折叠等

更新

引用自C++14规范的N4140草案:

5.8轮班操作员【经验轮班】

如果右操作数为负数,或大于或等于提升后的左操作数的位长度。

你们有没有非模板的解决方案?

您可能会使用部分专业化(如果警告困扰您(,灵感来自boost enable_if:

template <bool, unsigned N>
struct umask32 {
    static const uint32_t val = (uint32_t(1) << N) - 1;
};
template <unsigned N>
struct umask32<true, N> {
    static const uint32_t val = ~uint32_t(0);
};
template <unsigned N>
uint32_t mask(uint32_t x) {
    const uint32_t MASK = umask32<(N >= 32), N>::val;
    return x & MASK;
}

如果是N>=32,则将使用第二个umask32结构,否则将使用第一个结构,从而避免移位>=32位的代码。

您可以为允许N定义一个版本,为不允许的版本定义另一个版本:

using my_unsigned = unsigned;
template <my_unsigned N, typename std::enable_if<(N < 8 * sizeof(my_unsigned))>::type* = nullptr >
uint32_t mask(uint32_t x) {
  const uint32_t MASK = (uint32_t(1) << N) - 1;
  return x & MASK;
}
template <my_unsigned N, typename std::enable_if<(N >= 8 * sizeof(my_unsigned))>::type* = nullptr >
uint32_t mask(uint32_t x) {
  return x;
}

由于SFINAE,当N < 2^sizeof(unsigned)时,第一个版本将被实例化,否则编译器将选择(愚蠢的(第二个版本。