2012-06-13 13 views
39

Czy istnieje sposób na specjalizację szablonu opartą na zakresie wartości zamiast jednego? Wiem, następujący kod jest nieprawidłowy kod C++, ale pokazuje, co chciałbym zrobić. Piszę kod dla maszyny 8-bitowej, więc istnieje różnica w szybkości korzystania z int i znaków.Jak mogę wyspecjalizować szablon C++ dla zakresu wartości całkowitych?

template<unsigned SIZE> 
class circular_buffer { 
    unsigned char buffer[SIZE]; 
    unsigned int head; // index 
    unsigned int tail; // index 
}; 

template<unsigned SIZE <= 256> 
class circular_buffer { 
    unsigned char buffer[SIZE]; 
    unsigned char head; // index 
    unsigned char tail; // index 
}; 

Odpowiedz

45

Spróbuj std::conditional:

#include <type_traits> 

template<unsigned SIZE> 
class circular_buffer { 

    typedef typename 
     std::conditional< SIZE < 256, 
          unsigned char, 
          unsigned int 
         >::type 
     index_type; 

    unsigned char buffer[SIZE]; 
    index_type head; 
    index_type tail; 
}; 

Jeśli kompilator nie obsługuje jeszcze tej części C++ 11, nie ma odpowiednika w boost libraries.

Z drugiej strony łatwo jest przetoczyć własny (zasługa KerrekSB):

template <bool, typename T, typename F> 
struct conditional { 
    typedef T type; 
}; 

template <typename T, typename F> // partial specialization on first argument 
struct conditional<false, T, F> { 
    typedef F type; 
}; 
+1

Muszę powiedzieć, że jest to przydatne. – chris

+6

Cecha taka jak "std :: conditional" jest trywialna, aby pisać siebie, więc nie potrzebujesz żadnych bibliotek, jeśli ich nie masz: 'szablon struct conditional {typedef typ T; }; szablon struct conditional {typedef U type; }; ' –

+0

Pozdrawiam @KerrekSB, dodałem go do odpowiedzi. – jrok

33

Zastosowanie dodatkowy parametr: domyślnie bool

// primary template handles false 
template<unsigned SIZE, bool IsSmall = SIZE <= 256> 
class circular_buffer { 
    unsigned char buffer[SIZE]; 
    unsigned int head; // index 
    unsigned int tail; // index 
}; 

// specialization for true 
template<unsigned SIZE> 
class circular_buffer<SIZE, true> { 
    unsigned char buffer[SIZE]; 
    unsigned char head; // index 
    unsigned char tail; // index 
}; 
+0

byłbym szczęśliwszy jeśli 'IsSmall = SIZE <= 256' miał w nim jakieś parens –

+0

@MooingDuck w ten sposób? 'IsSmall = (ROZMIAR <= 256)' –

+3

@MooingDuck nie ma potrzeby. '<' jest mniejsze niż domyślnie. Dlatego istnieje disambiguator 'template'. Mylące '>' jest domyślnie bliskie szablonu i potrzebujesz do tego parens. C++ jest świetny, prawda? –

7

Inna możliwość:

template <unsigned SIZE> 
struct offset_size { 
    typedef typename offset_size<SIZE - 1>::type type; 
}; 

template <> 
struct offset_size<0> { 
    typedef unsigned char type; 
}; 

template <> 
struct offset_size<257> { 
    typedef unsigned int type; 
}; 

template<unsigned SIZE> 
class circular_buffer { 
    unsigned char buffer[SIZE]; 
    typename offset_size<SIZE>::type head; // index 
    typename offset_size<SIZE>::type tail; // index 
}; 

(Ideone example)

+7

Jesteś takim sadystą, to takie okrutne w.r.t. kompilator. Spróbuj skompilować 'offset_size ', kompilator będzie musiał utworzyć egzemplarze miliardów klas szablonów! – valdo

+0

Rzeczywiście, jeśli nie będziesz ostrożny z tą techniką, uruchomisz 'error: głębokość instancji szablonu przekracza maksimum ___' (domyślnie 500 na gcc). – cdhowie

0

Nienawidzę jak niechlujny czynienia z typów można więc zaproponować coś nieco prostsze, wykorzystując constexpr. Wariant ten pozwala na odmienne zachowanie, gdy nie jest wymagane różnym rodzaju i jest odpowiedzią na potrzebę dopasowania w przedziale, a nie tylko jedna strona wartości:

template<bool> struct If; 

constexpr bool InClosedRange(std::size_t Value, std::size_t Lower, std::size_t Upper) 
{ 
    return (Lower <= Value) && (Value <= Upper); 
} 

// Usage: 
template<size_t Width, If<true>> 
class Foo; 

template<size_t Width, If<InClosedRange(Width, 1, 41)>> 
class Foo { /* ... */ }; 

template<size_t Width, If<InClosedRange(Width, 42, 142)>> 
class Foo { /* ... */ }; 

Zainspirowany: https://stackoverflow.com/a/9516959/929315

Powiązane problemy