2012-06-13 16 views
39

¿Hay alguna manera de tener una especialización de plantilla basada en un rango de valores en lugar de solo uno? Sé que el siguiente código no es válido código C++, pero muestra lo que me gustaría hacer. Estoy escribiendo código para una máquina de 8 bits, por lo que hay una diferencia en la velocidad para usar ints y caracteres.¿Cómo puedo especializar una plantilla de C++ para un rango de valores enteros?

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

Respuesta

45

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

Si su compilador todavía no soporta esta parte de C++ 11, no hay equivalente en boost libraries.

Por otra parte, es fácil rodar su propia (el crédito va a 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

Tengo que decir, esto es útil. – chris

+6

Un rasgo como 'std :: conditional' es trivial para escribir usted mismo, por lo que realmente no necesita ninguna biblioteca si no la tiene:' template struct conditional {typedef T type; }; template struct conditional {typedef U type; }; ' –

+0

Cheers @KerrekSB, lo agregué a la respuesta. – jrok

33

Uso de pagar un extra bool parámetro:

// 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

sería más feliz si 'IsSmall = SIZE <= 256' tenía parens en algún lugar –

+0

@MooingDuck así? 'IsSmall = (SIZE <= 256)' –

+3

@MooingDuck no es necesario. '<' es menor que por defecto. Es por eso que está la desambigadora 'template'. Confusamente, '>' es una plantilla cercana por defecto y necesitas parens para eso. C++ es genial, ¿no? –

7

Otra opción posible:

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

Eres un sádico, es tan cruel w.r.t. el compilador Intenta compilar 'offset_size ', ¡el compilador tendrá que instanciar miles de millones de clases de plantilla! – valdo

+0

De hecho, si no tiene cuidado con esta técnica activará 'error: la profundidad de la instanciación de la plantilla excede el máximo de ___' (500 por defecto en gcc). – cdhowie

0

me gusta cómo tratar desordenado con tipos pueden ser tan propongo algo un poco más simple, aprovechando constexpr. Esta variante permite un comportamiento diferente cuando no se requiere un tipo variable y responde a la necesidad de encajar dentro de un rango y no sólo un lado de un valor:

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 { /* ... */ }; 

Inspirado por: https://stackoverflow.com/a/9516959/929315

Cuestiones relacionadas