2010-03-12 30 views
12

Supongamos que tenemos una función de plantilla "foo":Una especialización de plantilla para múltiples clases

template<class T> 
void foo(T arg) 
{ ... } 

PUEDO especialización por algún tipo particular, por ejemplo,

template<> 
void foo(int arg) 
{ ... } 

Si quería utilizar la misma especialización para todos los tipos internos numéricos (int, float, double etc.) que iba a escribir estas líneas muchas veces. Sé que ese cuerpo puede ser expulsado a otra función y solo llamar a esto se hará en el cuerpo de cada especialización, sin embargo, sería mejor si pudiera evitar escribir este "vacío foo (..." para cada tipo. cualquier posibilidad de decirle al compilador que quiero utilizar esta especialización para todo este tipo?

+3

¿Cuál es su objetivo final? –

+0

Esto no es directamente una respuesta, pero cubre la especialización frente a la sobrecarga y podría interesarle: http://www.gotw.ca/publications/mill17.htm – sellibitze

Respuesta

19

Puede usar std::numeric_limits para ver si un tipo es un tipo numérico (is_specialized es verdadero para todos los tipos fundamentales flotantes y enteros).

// small utility 
template<bool> struct bool2type { }; 

// numeric 
template<typename T> 
void fooImpl(T arg, bool2type<true>) { 

} 

// not numeric 
template<typename T> 
void fooImpl(T arg, bool2type<false>) { 

} 

template<class T> 
void foo(T arg) 
{ fooImpl(arg, bool2type<std::numeric_limits<T>::is_specialized>()); } 
+0

¡Idea genial! Pero ¿por qué no simplemente hacer que 'fooImpl' dependa de un tipo y un argumento booleano? – Vlad

+1

@Vlad porque queremos una compilación en tiempo de compilación. Si pasamos un argumento de función 'bool', tendremos el código ejecutado en la misma función y usaremos' if' para bifurcar. Desafortunadamente, este tipo comprobará las dos ramas, y es muy probable que se pierda el objetivo. –

+0

@Johannes: Quise decir dos argumentos de plantilla, solo para ahorrar la definición 'bool2type'. – Vlad

0

tal vez usted puede definir una función de plantilla por defecto que funciona en todo tipo nativo, y delegar especialización de tipo personalizado al usuario

3

Puede usar un enfoque con preprocesador.

foo.inc:

template<> 
void foo(TYPE arg) 
{ /* do something for int, double, etc. */ } 

foo.h:

template<class T> 
void foo(T arg) 
{ /*do something */ } 

#define TYPE int 
#include "foo.inc" 
#undef TYPE 

#define TYPE double 
#include "foo.inc" 
#undef TYPE 

etc.

3

Con impulso:

#include <boost/type_traits/is_scalar.hpp> 
#include <iostream> 
#include <string> 

namespace detail 
{ 
    typedef const boost::true_type& true_tag; 
    typedef const boost::false_type& false_tag; 

    template <typename T> 
    void foo(const T& pX, true_tag) 
    { 
     std::cout << "special: " << pX << std::endl; 
    } 

    template <typename T> 
    void foo(const T& pX, false_tag) 
    { 
     std::cout << "generic: " << pX << std::endl; 
    } 
} 

template <typename T> 
void foo(const T& pX) 
{ 
    detail::foo(pX, boost::is_scalar<T>()); 
} 

int main() 
{ 
    std::string s = ":D"; 
    foo(s); 
    foo(5); 
} 

Usted puede fácilmente sobre todo hacerlo sin impulso :

#include <iostream> 
#include <string> 

// boolean stuff 
template <bool B> 
struct bool_type {}; 

typedef bool_type<true> true_type; 
typedef bool_type<false> false_type; 

// trait stuff 
template <typename T> 
struct is_scalar : false_type 
{ 
    static const bool value = false; 
}; 

#define IS_SCALAR(x) template <> \ 
      struct is_scalar<x> : true_type \ 
      { \ 
       static const bool value = true; \ 
      }; 

IS_SCALAR(int) 
IS_SCALAR(unsigned) 
IS_SCALAR(float) 
IS_SCALAR(double) 
// and so on 

namespace detail 
{ 
    typedef const true_type& true_tag; 
    typedef const false_type& false_tag; 

    template <typename T> 
    void foo(const T& pX, true_tag) 
    { 
     std::cout << "special: " << pX << std::endl; 
    } 

    template <typename T> 
    void foo(const T& pX, false_tag) 
    { 
     std::cout << "generic: " << pX << std::endl; 
    } 
} 

template <typename T> 
void foo(const T& pX) 
{ 
    detail::foo(pX, is_scalar<T>()); 
} 

int main() 
{ 
    std::string s = ":D"; 
    foo(s); 
    foo(5); 
} 
0

Puede escribir un pequeño script (por ejemplo, Perl) para generar el archivo de origen para usted. Cree una matriz que contenga todos los tipos que desea especializar, y haga que escriba el encabezado de la función para cada uno. Incluso puede incrustar la ejecución del script en su archivo MAKE para volver a ejecutar automágicamente si cambia algo.

Nota: esto supone que la implementación de foo se puede hacer trivial y similar para cada tipo, por ejemplo, simplemente llamando a la función de implementación real. Pero evita un montón de palabrería de plantilla/preprocesador que podría hacer que un futuro mantenedor se rascara la cabeza.

Cuestiones relacionadas