2010-03-16 19 views
12

Tengo una clase de plantilla básica, pero me gustaría restringir el tipo de la especialización a un conjunto de clases o tipos. e.g .:Tipo de fuerza de la plantilla C++

template <typename T> 
class MyClass 
{ 
.../... 
private: 
    T* _p; 
}; 

MyClass<std::string> a; // OK 
MYCLass<short> b;  // OK 
MyClass<double> c;  // not OK 

Esos son solo ejemplos, los tipos permitidos pueden variar.

¿Eso es posible? Si es así, ¿cómo hacerlo?

Gracias.

+1

Tengo curiosidad, la caja tiene usted para prohibir alguna instanciación de la plantilla de qué sirve? No puedo entender por qué no me gustaría que mi código sea reutilizado: -/ – Seb

+0

¿Qué significa "puede variar"? ¿Pueden variar en el momento de la compilación o simplemente ser diferentes que en el ejemplo? – foraidt

+3

@Seb: es posible que la plantilla no funcione correctamente con ciertos tipos. Puede no compilarse (en cuyo caso puede ser deseable un mensaje de error más limpio que el compilador que genere), o puede compilar pero no tiene la semántica deseada (en cuyo caso es bueno evitar que se compile) – jalf

Respuesta

17

Otra versión es dejarlo indefinido para los tipos prohibidos

template<typename T> 
struct Allowed; // undefined for bad types! 

template<> struct Allowed<std::string> { }; 
template<> struct Allowed<short> { }; 

template<typename T> 
struct MyClass : private Allowed<T> { 
    // ... 
}; 

MyClass<double> m; // nono 
+2

+1. De hecho, me gusta esta solución mejor que la primera respuesta. – paercebal

+0

@paercebal, gracias amigo xD –

+0

De nada. De todos modos, desde mi último comentario, tu respuesta se convirtió en la respuesta seleccionada, así que, al parecer, no soy el único en ver sus valores ... :-P – paercebal

1

Hay varios trucos que permiten comprobar si hay algunas cosas, dependiendo de cuáles son sus criterios para la creación de instancias es ser permitido o no. En la práctica, debería usar una biblioteca de palancas más alta para aquellos como Boost's Concept Check.

8

Yust una idea rápida, estoy seguro que hay mejores enfoques:

template <typename T> struct protector { 
static const int result = 1; 
}; 

template <> struct protector<double> { 
static const int result = -1; 
}; 

template <typename T> 
class MyClass 
{ 
    private: 
    char isfine[protector<T>::result]; 
}; 

Podría ser mejor, sin embargo, para poner un comentario grasa sobre su código para evitar que los usuarios crear instancias de los tipos incorrectos: -)

+0

Usted golpea yo a eso. El modismo utilizado aquí se llama * rasgos de tipo *. –

+4

¿Hay alguna otra manera de hacerlo sin incurrir en el costo de una matriz char [1] en cada instancia? Personalmente, usaría BOOST_STATIC_ASSERT. –

+2

Agrega 'typedef' antes de' char'. – Ari

1

no estoy seguro de esto, pero se puede añadir otra especialización de plantilla para el doble plantilla

class MyClass 
{ 
.../... 
private: 
    T* _p; 
}; 

template <double> class MyClass 
{}; 

que trabajaría para usted r ejemplo, pero no para el caso general.

En general, agregaría una declaración de compilación para verificar los tipos no deseados.

Espero que ayude.

2

En general, no es necesario restringir con qué tipos de plantillas se pueden crear instancias. O bien la plantilla es compilable con el tipo dado (y funciona bien) o no (y produce un error de compilación sin ningún esfuerzo por parte del programador).


Si usted necesita para poner en restricciones, en general, los tipos tienen algo en común que puede ser descrito por algunos rasgos de tipo que ya están disponibles (biblioteca estándar, boost::type_traits), o puede crear un nuevo rasgo de tipo para ellos.

Por ejemplo, aquí hay una clase de plantilla que solo permite tipos enteros, usando std::numeric_limits para verificarla (si escribe su propio tipo numérico, puede especializarlo para que también funcione con su nuevo tipo entero). static_assert es C++ 0x solamente, si no está disponible use BOOST_STATIC_ASSERT o algún otro truco.

#include <limits> 
#include <string> 

template <class T> 
class X 
{ 
    static_assert(std::numeric_limits<T>::is_integer, "X can be only instantiated with integer types"); 
    //... 
}; 

int main() 
{ 
    X<int> xi; 
    X<char> xc; 
    //X<double> xd; 
    //X<std::string> xs; 
} 

Si usted sólo va a apoyar a un puñado de tipos arbitrarios que no tienen nada en común (como se desprende de su ejemplo hipotético), una forma es emplear TypeListas. Nuevamente, aumentar la tarea puede hacer que la tarea sea mucho más fácil, pero así es cómo puede hacer la suya (esto solo va a la mitad, se requerirá trabajo adicional para hacer que la lista de tipos sea más bonita).

struct no_type {}; 

template <class T, class U = no_type> 
struct t_list 
{ 
    typedef T head; 
    typedef U tail; 
}; 

//trait to check if two types are identical 
template <class T, class U> 
struct is_same 
{ 
    static const bool value = false; 
}; 

template <class T> 
struct is_same<T, T> 
{ 
    static const bool value = true; 
}; 

//compile-time recursion to check if T matches any type in list L 
template <class T, class L> 
struct in_type_list 
{ 
    static const bool value = 
     is_same<T, typename L::head>::value || in_type_list<T, typename L::tail>::value; 
}; 

//terminates recursion 
template <class T> 
struct in_type_list<T, no_type> 
{ 
    static const bool value = false; 
}; 

template <class T> 
class X 
{ 
    typedef t_list<double, t_list<int, t_list<char> > > allowed_types; //double, int, char 

    //poor man's static_assert 
    typedef int check_type [in_type_list<T, allowed_types>::value ? 1 : -1]; 
    //... 
}; 

int main() 
{ 
    X<char> xc; 
    X<int> xi; 
    X<double> xd; 
    //X<float> xf; 
} 
Cuestiones relacionadas