这是一个愚蠢的问题,部分是为了好玩。
我有一个“明确定义的”(或“饱和”?)位掩码函数
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]
如果右操作数,则行为未定义 是负的,或大于或等于提升的左操作数的位数。
你们有没有非模板解决方案?
答案 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)
时,第一个版本将被实例化,否则编译器将选择(哑)第二个版本。