5

我正在用 clang 3.3 编译一些似乎可以用 gcc 4.8 编译的代码:

原始代码是:

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);
};

在铿锵声中,如果我尝试声明 test<16,16> 或 test<8,0> 我会收到错误消息:

test.cpp:41:34:错误:constexpr 变量“frac_mask”必须由常量表达式初始化

static constexpr std::size_t frac_mask = ~((~value_type(0)) << F);

如果我将代码转换为:

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);
};

它在大多数情况下都可以编译(I、F 的值),但如果我声明 test<8, 0>,我会收到错误消息:

test.cpp:23:36:错误:constexpr 变量“frac_mask”必须由常量表达式初始化

test.cpp:66:15:注意:在此处请求的模板类 'test<8, 0>' 的实例化中

test.cpp:23:66:注意:负值左移-1

   static constexpr mask_type frac_mask = ~((~mask_type(0)) << F);

我的问题是 - 就 constexpr 的规范而言,我在这里违反了一些规则吗?此外,对于最后一个错误 - 掩码类型是无符号的 - 这是一个编译器问题,它认为我正在转移一个负值还是我误读了代码?

4

1 回答 1

3

在第一种情况下,您会导致签名溢出。C++11 5.19/2 中列出的表达式不是常量表达式的条件之一是它涉及

未在数学上定义或不在其类型的可表示值范围内的结果

通过使用定义为使用模运算的无符号类型,结果保持在范围内。据推测,GCC 对这条规则的严格程度不如 Clang。

在最后一种情况下,无符号 8 位类型被提升为int,而不是无符号类型,因此您再次得到有符号溢出。您可以通过在否定后转换回无符号类型来解决此问题:

static constexpr mask_type frac_mask = ~(mask_type(~mask_type(0)) << F);

虽然我不太确定,也没有安装 Clang 来测试。

于 2013-08-27T15:27:40.593 回答