2010-03-29 8 views
7

Betrachten Sie das folgende Beispiel:Wie kann ich eine Funktionsvorlage für alle Typen mit einem bestimmten Typmerkmal schreiben?

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>(); 
} 

Die Scanner Klasse verwendet wird Token von einer Quelle zu extrahieren. Der obige Code funktioniert gut, aber schlägt fehl, wenn ich versuche, andere integrale Typen wie oder unsigned int zu get. Der Code zum Lesen dieser Typen entspricht genau dem Code zum Lesen einer int. Ich könnte einfach den Code für alle anderen integralen Typen kopieren, die ich gerne lesen würde, aber ich würde lieber eine Funktionsvorlage für alle integralen Typen definieren.

Ich habe versucht, die folgenden:

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

Welche funktioniert wie ein Charme, aber ich bin nicht sicher, wie Scanner::get<string>() zu bekommen wieder zu funktionieren. Also, wie kann ich Code schreiben, so dass ich scanner.get<string>() und scanner.get<any integral type>() tun kann und eine einzige Definition haben, um alle integralen Typen zu lesen?

Update: Bonus Frage: Was ist, wenn ich mehr als eine Reihe von Klassen basierend auf einigen Merkmalen akzeptieren möchte? Zum Beispiel: Wie sollte ich dieses Problem angehen, wenn ich drei get Funktionen haben möchte, die (i) integrale Typen (ii) Fließkommatypen (iii) Zeichenketten akzeptieren.

Antwort

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

-Update

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

Ich würde bemerken, dass Sie wahrscheinlich die 'boost :: mpl :: und_' und 'boost :: mpl :: or_' verwenden könnten, um die Argumente in 'disable_if' zu kombinieren. +1 nichtsdestotrotz :) –

+0

Sie können auch 'ice_and' und' ice_or' aus der Boost-Bibliothek verwenden. –

3

Auf eine andere Vorlage verschieben. Hier ist das allgemeine Muster für das, was Sie wollen: „Was passiert, wenn ich mehr als eine Reihe von Klassen auf einigen Zügen basierend akzeptieren will“

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

Gute Idee, Indirektion auf diese Weise hinzuzufügen. Vielleicht würde es sogar noch flexibler werden, wenn Sie kein 'bool' verwenden, sondern ein' enum' als Merkmalsschalter. – xtofl

+0

@xtofl: Ja. Das würde auch für die Bonusfrage ausreichen. –