明确定义的位移超过32位无编译器警告

时间:2015-08-18 08:44:48

标签: c++ bit-manipulation template-meta-programming

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

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

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

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

请建议我摆脱uint32_t(1) << 1234的一些有点蠢蠢的技巧(以及模板元编程?)。

我有GCC 4.9(部分)支持C ++ 14并且足够聪明以进行常量折叠等

更新

引自C41 14规范的N4140草案:

5.8移位运算符[expr.shift]

  

如果右操作数,则行为未定义   是负的,或大于或等于提升的左操作数的位数。

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

2 个答案:

答案 0 :(得分:3)

你可以使用部分特化(如果警告困扰你),灵感来自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位移位的代码。

答案 1 :(得分:0)

您可以为承认的 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)时,第一个版本将被实例化,否则编译器将选择(哑)第二个版本。