2013-08-27 17 views
5

Sto compilando un codice con clangore 3.3 che sembra per compilare bene con gcc 4.8:clang 3.3 e constexpr vincoli

Il codice originale è stato:

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, se tento di dichiarare Test < 16,16> o il test < 8,0> ottengo l'errore:

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

gioco intorno con esso, se posso convertire il codice a:

C'è alcune regole -

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

La mia domanda è:

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

Compila nella maggior parte dei casi (valori di I, F), ma se dichiaro di prova < 8, 0>, ottengo l'errore Sto violando qui in termini di specifiche di constexpr? Inoltre, per l'ultimo errore - il tipo di maschera è senza segno - si tratta di un problema del compilatore che pensa che io stia spostando un valore negativo o sto leggendo male il codice?

+0

possibile duplicato di [Calling constexpr nell'argomento modello predefinito] (http://stackoverflow.com/questions/10721130/calling-constexpr-in-default-template-argument) –

+0

Immettere le regole di promozione convertendo il tuo uint8_t in int, che è firmato? –

risposta

3

Nel primo caso, stai causando un overflow firmato. Una delle condizioni per un'espressione non di essere un'espressione costante, elencati C++ 11 5.19/2, è che si tratta

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

Utilizzando un tipo senza segno, che viene definito per utilizzare aritmetica modulare , il risultato rimane nell'intervallo. Presumibilmente, GCC è meno rigido di questa regola rispetto a Clang.

Nel caso finale, il tipo di 8 bit senza segno viene promosso a int, non un tipo senza segno, quindi si ottiene nuovamente l'overflow con segno. Probabilmente si può rimediare convertendo di nuovo al tipo unsigned dopo negando:

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

anche se non sono del tutto sicuro di questo, e non hanno un impianto Clang per testare con.

+0

Questo sembra risolverlo - puoi spiegarmi perché bitwise non di un tipo senza segno sarebbe promosso a int? – user2721897

+0

@ user2721897: le regole di promozione specificano che è promosso a 'int' se può rappresentare tutti i valori del tipo originale; che è il caso per qualsiasi tipo più piccolo, firmato o non firmato. –

+0

@ user2721897 Il char non firmato viene promosso a int * prima * viene applicato il ~ (o qualsiasi operatore aritmetico). Ciò significa che ad es. 'intvar greggo

Problemi correlati