2013-08-27 17 views
5

Mam kompilacji kodu z brzękiem 3.3, który wydaje się skompilować grzywny z gcc 4.8:Clang 3.3 i constexpr ograniczenia

Oryginalny kod był:

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

W brzękiem, jeśli próbuję zadeklarować Test < 16,16> 8,0 lub test <> I pojawia się błąd:

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

zabawy z nim, jeśli przekonwertować kod do:

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

To kompiluje w większości przypadków (wartości I, F), ale jeśli Oświadczam testu < 8, 0>, pojawia się błąd:

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

Moje pytanie brzmi - czy istnieje pewne zasady Czy naruszam tutaj warunki specyfikacji constexpr? Ponadto w przypadku ostatniego błędu - typ maski jest niepodpisany - czy jest to problem kompilatora, który uważa, że ​​przenoszę wartość ujemną, czy też błędnie odczytuję kod?

+0

możliwy duplikat [Calling constexpr w domyślnym argumencie szablonu] (http://stackoverflow.com/questions/10721130/calling-constexpr-in-default-template-argument) –

+0

Wpisz zasady promocji, konwertując twój uint8_t na int, który jest podpisany? –

Odpowiedz

3

W pierwszym przypadku powoduje się podpisane przepełnienie. Jednym z warunków wyrażenia nie być stała ekspresja wymienionych w C++ 11 5,19/2, jest to, że wiąże się

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

Przy użyciu unsigned, która została zdefiniowana modułową arytmetyki , wynik pozostaje w zasięgu. Przypuszczalnie GCC jest mniej rygorystyczne w tej regule niż Clang.

W ostatnim przypadku nieobsługiwany typ 8-bitowy jest promowany do int, a nie typu bez znaku, więc ponownie otrzymasz podpisane przepełnienie. Prawdopodobnie można naprawić poprzez konwersję z powrotem do typu unsigned po negując:

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

choć nie jestem pewien, o tym, i nie ma instalacji do przetestowania z brzękiem.

+0

Wydaje się, że to naprawiasz - czy możesz mi wytłumaczyć, dlaczego bitowe, a nie niepodpisane, byłoby promowane do int? – user2721897

+0

@ user2721897: Zasady promocji określają, że jest on promowany do 'int', jeśli może reprezentować wszystkie wartości oryginalnego typu; co ma miejsce w przypadku każdego mniejszego typu, podpisanego lub niepodpisanego. –

+0

@ user2721897 Niepodpisany znak char jest promowany na int * przed zastosowaniem * the ~ (lub dowolnego operatora arytmetycznego). Oznacza to, że np. 'intvar greggo