2013-08-27 2 views
5

Я компиляция кода с лязгом 3.3, который, кажется компилироваться с GCC 4.8:Clang 3.3 и constexpr ограничение

Исходный код был:

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

В звоне, если я пытаюсь объявить тест < 16,16> или тест < 8,0> Я получаю сообщение об ошибке:

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

играть с ним, если я преобразовать код:

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), но если я объявляю тест < 8, 0>, я получаю ошибку:

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

Мой вопрос - есть ли какие-то правила Я нарушаю здесь с точки зрения спецификации constexpr? Кроме того, для последнего типа маски ошибки является unsigned - это проблема компилятора, которая, по моему мнению, меняет отрицательное значение или я неправильно читаю код?

+0

Возможный дубликат [Вызов constexpr в аргументе шаблона по умолчанию] (http://stackoverflow.com/questions/10721130/calling-constexpr-in-default-template-argument) –

+0

Правила продвижения по типу преобразования, преобразующие ваш uint8_t в int, который подписывается? –

ответ

3

В первом случае вы вызываете переполнение. Одним из условий для выражения не быть постоянным выражением, перечисленное в C++, 11 5,19/2, является то, что она включает в себя

a result that is not mathematically defined or not in the range of representable values for its type

При использовании без знака типа, который определен для использования модульной арифметики , результат остается в диапазоне. Предположительно, GCC менее строг в отношении этого правила, чем Clang.

В последнем случае беззнаковый 8-разрядный тип повышается до int, а не без знака, поэтому вы снова получаете переполнение подписей. Вы, вероятно, можно исправить путем преобразования обратно в беззнакового после отрицая:

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

хотя я не совсем уверен, что и не имеют установки Clang для тестирования с.

+0

Это, кажется, исправить - можете ли вы объяснить мне, почему побитовое число без знака будет повышаться до int? – user2721897

+0

@ user2721897: Правила продвижения указывают, что он продвигается до 'int', если он может представлять все значения исходного типа; что имеет место для любого меньшего типа, подписанного или неподписанного. –

+0

@ user2721897 Беззнаковый char продвигается до int * до того, как применяется * (или любой арифметический оператор). Это означает, что, например, 'intvar greggo

Смежные вопросы