I'm compiling some code with clang 3.3 that seems to compile fine with gcc 4.8:
The original code was:
template <std::size_t N> struct helper { typedef void type; };
template <> struct helper<64> { typedef int64_t type; };
template <> struct helper<32> { typedef int32_t type; };
template <> struct helper<16> { typedef int16_t type; };
template <> struct helper<8> { typedef int8_t type; };
template <std::size_t I, std::size_t F>
struct test
{
typedef typename helper<I+F>::type value_type;
static constexpr std::size_t frac_mask = ~((~value_type(0)) << F);
};
In clang, if I attempt to declare test<16,16> or test<8,0> I get the error:
test.cpp:41:34: error: constexpr variable 'frac_mask' must be initialized by a constant expression
static constexpr std::size_t frac_mask = ~((~value_type(0)) << F);
Playing around with it, if I convert the code to:
template <std::size_t I, std::size_t F>
struct test
{
typedef typename helper<I+F>::type value_type;
typedef typename std::make_unsigned<value_type>::type mask_type;
static constexpr mask_type frac_mask = ~((~mask_type(0)) << F);
};
It compiles in most cases (values of I, F), but if I declare test<8, 0>, I get the error:
test.cpp:23:36: error: constexpr variable 'frac_mask' must be initialized by a constant expression
test.cpp:66:15: note: in instantiation of template class 'test<8, 0>' requested here
test.cpp:23:66: note: left shift of negative value -1
static constexpr mask_type frac_mask = ~((~mask_type(0)) << F);
My question is - is there some rules I'm violating here in terms of the specification of constexpr? Also, for the last error - mask type is unsigned - is this a compiler issue that it thinks I'm shifting a negative value or am I misreading the code?
In the first case, you're causing signed overflow. One of the conditions for an expression not to be a constant expression, listed in C++11 5.19/2, is that it involves
a result that is not mathematically defined or not in the range of representable values for its type
By using an unsigned type, which is defined to use modular arithmetic, the result remains in range. Presumably, GCC is less strict about this rule than Clang.
In the final case, the unsigned 8-bit type is promoted to int
, not an unsigned type, so you get signed overflow again. You can probably fix that by converting back to the unsigned type after negating:
static constexpr mask_type frac_mask = ~(mask_type(~mask_type(0)) << F);
although I'm not quite sure about that, and don't have a Clang installation to test with.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With