2010-03-29 12 views
7

Prenons l'exemple suivant:Comment puis-je écrire un modèle de fonction pour tous les types avec un trait de type particulier?

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 classe Scanner est utilisé pour extraire des jetons d'une source. Le code ci-dessus fonctionne très bien, mais échoue lorsque j'essaie d'autres types intégraux comme un char ou un unsigned int. Le code pour lire ces types est exactement le même que le code pour lire un int. Je pourrais juste dupliquer le code pour tous les autres types intégraux que j'aimerais lire, mais je préfèrerais définir un modèle de fonction pour tous les types entiers.

J'ai essayé ce qui suit:

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

Ce qui fonctionne comme un charme, mais je ne suis pas sûr comment obtenir Scanner::get<string>() à fonctionner à nouveau. Alors, comment puis-je écrire du code afin que je puisse faire scanner.get<string>() et scanner.get<any integral type>() et avoir une seule définition pour lire tous les types intégrales?

Mise à jour: question de bonus: Que se passe-t-il si je veux accepter plus d'une plage de classes en fonction de certains traits? Par exemple: comment dois-je aborder ce problème si je veux avoir trois fonctions get acceptant (i) des types entiers (ii) des types à virgule flottante (iii), respectivement.

Répondre

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

Mise à jour

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

Je remarquerais que vous pourriez probablement utiliser les commandes boost :: mpl :: and_ et boost :: mpl :: or_ pour combiner les arguments de 'disable_if'. +1 néanmoins :) –

+0

Vous pouvez également utiliser 'ice_and' et' ice_or' de la bibliothèque Boost. –

3

Déférer à un autre modèle. Voici le schéma général pour ce que vous voulez: « Et si je veux accepter plus d'une gamme de classes en fonction de certains traits »

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

Bonne idée d'ajouter une indirection de cette façon. Peut-être que cela deviendrait encore plus flexible si vous n'utilisiez pas 'bool', mais un' enum' comme trait-switch. – xtofl

+0

@xtofl: Oui. Cela répondrait à la question de bonus aussi. –