2010-03-29 10 views
7

Consideremos el siguiente ejemplo:¿Cómo puedo escribir una plantilla de función para todos los tipos con un rasgo de tipo particular?

struct Scanner 
{ 
    template <typename T> 
    T get(); 
}; 

template <> 
string Scanner::get() 
{ 
    return string("string"); 
} 

template <> 
int Scanner::get() 
{ 
    return 10; 
} 

int main() 
{ 
    Scanner scanner; 
    string s = scanner.get<string>(); 
    int i = scanner.get<int>(); 
} 

La clase Scanner se utiliza para extraer tokens de alguna fuente. El código anterior funciona bien, pero falla cuando trato de get otros tipos integrales como char o unsigned int. El código para leer estos tipos es exactamente el mismo que el código para leer un int. Podría duplicar el código para todos los otros tipos integrales que quisiera leer, pero prefiero definir una plantilla de función para todos los tipos integrales.

He intentado lo siguiente:

struct Scanner 
{ 
    template <typename T> 
    typename enable_if<boost::is_integral<T>, T>::type get(); 
}; 

que funciona como un encanto, pero no estoy seguro cómo conseguir Scanner::get<string>() a funcionar de nuevo. Entonces, ¿cómo puedo escribir código para poder hacer scanner.get<string>() y scanner.get<any integral type>() y tener una definición única para leer todos los tipos integrales?

Actualización: pregunta de bonificación: ¿Qué sucede si quiero aceptar más de un rango de clases en función de algunos rasgos? Por ejemplo: cómo abordar este problema si quiero tener tres funciones get que acepten (i) tipos enteros (ii) cadenas de tipos de coma flotante (iii), respectivamente.

Respuesta

10
struct Scanner 
{ 
    template <typename T> 
    typename boost::enable_if<boost::is_integral<T>, T>::type get() 
    { 
     return 10; 
    } 
    template <typename T> 
    typename boost::disable_if<boost::is_integral<T>, std::string>::type get() 
    { 
     return "string"; 
    } 
}; 

actualización

struct Scanner 
{ 
    template <typename T> 
    typename boost::enable_if<boost::is_integral<T>, T>::type get() 
    { 
     return 10; 
    } 

    template <typename T> 
    typename boost::enable_if<boost::is_floating_point<T>, T>::type get() 
    { 
     return 11.5; 
    } 

    template <typename T> 
    std::string get(
      typename boost::disable_if<boost::is_floating_point<T>, T>::type* = 0, 
      typename boost::disable_if<boost::is_integral<T>, T>::type* = 0) 

    { 
     return std::string("string"); 
    } 
}; 
+2

Quiero decir que probablemente puedas usar 'boost :: mpl :: and_' y' boost :: mpl :: or_' para combinar los argumentos en 'disable_if'. +1, no obstante :) :) –

+0

También podría usar 'ice_and' y' ice_or' de la biblioteca de Boost. –

3

Deferir a otra plantilla. Aquí está el patrón general de lo que quiere: "¿Qué pasa si quiero aceptar más de una gama de clases basadas en algunos rasgos"

template <typename T, bool HasTrait = false> 
struct scanner_impl; 

template <typename T> 
struct scanner_impl 
{ 
    // Implement as though the trait is false 
}; 

template <typename T> 
struct scanner_impl<true> 
{ 
    // Implement as though the trait is true 
}; 

// This is the one the user uses 
template <typename T> 
struct scanner : scanner_impl<T, typename has_my_trait<T>::value> 
{ 
}; 
+1

Buena idea para agregar indirección de esta manera. Tal vez sería incluso más flexible si no usas un 'bool', sino un' enum' como un modificador de rasgos. – xtofl

+0

@xtofl: Sí. Eso también respondería por la pregunta de bonificación. –

Cuestiones relacionadas