2010-12-23 15 views
17

¿Es posible en C++ comprobar el tipo pasado a una función de plantilla? Por ejemplo:Switch pasó el tipo de plantilla

template <typename T> 
void Foo() 
{ 
    if (typeof(SomeClass) == T) 
     ...; 
    else if (typeof(SomeClass2) == T) 
     ...; 
} 

Respuesta

27

Sí, es ... pero probablemente no funcione de la manera esperada.

template < typename T > 
void foo() 
{ 
    if (is_same<T,SomeClass>::value) ...; 
    else if (is_same<T,SomeClass2>::value) ...; 
} 

Puede obtener is_same de std:: o boost:: función de su deseo/compilador. El primero solo está en C++ 0x.

El problema viene con lo que está en .... Si espera poder hacer alguna llamada a funciones específica para esos tipos dentro de foo, está tristemente equivocado. Se producirá un error de compilación aunque esa sección de código nunca se ejecute cuando se pasa algo que no obedece a esa interfaz esperada.

Para resolver ESE problema necesita hacer algo un poco diferente. Me gustaría recomendar la etiqueta de despacho:

struct v1_tag {}; 
struct v2_tag {}; 

template < typename T > struct someclass_version_tag; 
template < > struct someclass_version_tag<SomeClass> { typedef v1_tag type; }; 
template < > struct someclass_version_tag<SomeClass2> { typedef v2_tag type; }; 

void foo(v1_tag) { ... } 
void foo(v2_tag) { ... } 
template < typename T > void foo() 
{ 
    typedef typename someclass_version_tag<T>::type tag; 
    foo(tag()); 
} 

Tenga en cuenta que usted no estar sufriendo ningún tipo de gastos de ejecución polimorfismo aquí y con optimizaciones encendidos que debería dar lugar a la misma o incluso menor tamaño del código y la velocidad (aunque no deberías Te preocupes por eso de todos modos hasta que hayas ejecutado un generador de perfiles.

+0

¿Es posible devolver diferentes tipos basados ​​en version_tag? – Boying

+0

Claro. Necesitará encontrar la manera de consultar la información si está atrapado en C++ 03, o usará el modo automático en C++ 11 en adelante. Siempre que no intente devolver diferentes tipos dentro de la misma instanciación, no tendrá ningún problema. –

+0

ahora con C++ 17, puede usar declaraciones constexpr if para hacer esto trivialmente – xaxxon

10

Si usted quiere hacer algo específico en función del tipo, la plantilla se especializan:

template <typename T> 
void Foo() { } 

template <> 
void Foo<SomeClass>() { } 

template <> 
void Foo<SomeClass2>() { } 

// etc. 

(En realidad no desea especializarse la plantilla de función, sin embargo, lo que es solo para exposición. Si lo desea, puede sobrecargar la plantilla o delegarla en una plantilla de clase especializada. Para más información sobre por qué y cómo evitar plantillas de funciones especializadas, lea Herb Sutter's Why Not Specialize Function Templates?)

2

Sí. Deberá usar type traits. Por ejemplo:

#include <boost/type_traits/is_same.hpp> 

template <typename T> 
void Foo() 
{ 
    if ((boost::is_same<T, SomeClass>::value)) 
     ...; 
    else if ((boost::is_same<T, SomeClass2>::value)) 
     ...; 
} 

Dependiendo de lo que está tratando de lograr, utilizando template specialization podría ser mucho mejor opción.

Además, puede usar enable_if/disable_if para habilitar/deshabilitar ciertas funciones/métodos. Combinar esto con rasgos de tipo permitirá, por ejemplo, usar una función para un conjunto de tipos y otra función para otro conjunto de tipos.

+0

Tu código no se compilará. O necesita crear una instancia de la instanciación is_same como una variable, o necesita acceder a su definición interna de 'value'. –

+0

@Noah: Correcto. Este fue un consejo rápido y sucio :-) Lo he modificado. –

+0

¿Es posible hacer esto para un conjunto de valores? Por ejemplo, ¿hacer algo más por cada valor enum? – paulm

2

No, sin embargo se puede utilizar la especialización parcial:

template<typename T> 
struct Bar { static void foo(); }; 
template<typename T> 
template<> inline void Bar<T>::foo() { 
//generic 
} 
template<> inline void Bar<int>::foo() { 
//stuff for int 
} 
template<> inline void Bar<QString>::foo() { 
//QString 
} 

Editar Sí con caracteres de tipo, sin embargo, no es realmente necesario. Editar 2 type_traits example.

#include <type_traits> 
template<typename T> void foo() { 
    using std::is_same; 
    if<is_same<T, T2>::value || is_same<T, T1>::value) { 
     /* stuff */ 
    } 
} 
+0

¿Qué sucede si quiero hacer algo como 'if (is_same || is_same )'? –

+0

Luego debe usar los rasgos de tipo '#include y std :: is_same :: value' si su compilador admite C++ 0x (VS2010/cualquier gcc reciente) o simplemente use boost para la compatibilidad del compilador anterior. – OneOfOne

+3

Eso no es una especialización parcial, es una especialización completa. Hay una diferencia bastante significativa. Probablemente lo más significativo es que sería imposible hacer una especialización parcial de esa manera, ya que no está permitido con funciones de ningún tipo. –

Cuestiones relacionadas