2016-05-12 5 views
3

aufrufen Ich versuche, eine Vorlagefunktion zu erstellen, die zwei verschiedene Implementierungen basierend auf einem Vorlageargument hat. Der erste ist spezifisch für I/O-Manipulatoren und der zweite ist für jeden allgemeinen Fall.Wie templated Typ mit

template <typename T> 
typename std::enable_if< 
      std::is_same<decltype(std::setw), T>::value>::type 
foo(T& t){ 
     cout << "enabled" << std::endl; 
} 

template <typename T> 
void foo(T& t){ 
     cout << "Normal" << std::endl; 
} 

template <typename... Ts> 
void foohelper(Ts... t){ 
     foo(t...); 
} 
int main(){ 
     foohelper(std::setprecision(3)); // It should print enabled, but printing Normal 
} 

Momentan tut es nicht, was ich versuche zu implementieren. Wie kann ich es reparieren?

+1

std :: set und std :: setprecision sind nicht vom gleichen Typ (auch std :: setprecision (3) ist nicht der gleiche typ wie std :: setprecision) vielleicht, wenn Sie auf das Problem näher eingehen, können wir Ihnen geben eine Antwort! – bendervader

+0

Ja, ich meinte std :: setw. Sie sind tatsächlich anders, siehe den Quelltext hier http://cs.brown.edu/~jwicks/libstdc++/html_user/iomanip-source.html Sie geben eine Struktur zurück, aber jede Struktur hat einen anderen Namen! Probieren Sie dies aus, um 'std :: cout << typeid (declltype (std :: setw)) zu bestätigen. Name() << std :: endl; std :: cout << typeid (dekltyp (std :: setprecision)). Name() << std :: endl; ' – bendervader

Antwort

5

Sie müssen enable_if nicht verwenden - spezialisieren Sie einfach die Vorlage unterhalb der Hauptdefinition von template foo.

template <> 
void foo<decltype(std::setw(0))>(decltype(std::setw(0))& t) 
{ 
     cout << "enabled" << std::endl; 
} 
+0

Danke Es funktioniert, nicht ich verstehe nicht, warum ich die Definition unter Main halten muss? – AAA

+0

Das sieht gut aus, aber kann Spezialisierung in diesem Fall durch einfache Überladung ersetzt werden? –

+0

@AAA: schlechte Formulierung meinerseits - nicht unter der Funktion 'main()', aber unter der Hauptdefinition * der Schablone *, d. H. Unter 'Schablone void foo (T & t) ...'. Die Vorlage sollte vor den Spezialisierungen gesehen werden. –

2

Der Rückgabetyp dieser Funktionen ist nicht angegeben. Das bedeutet, dass Sie type_traits nicht verwenden können, um sie zu vergleichen. Als Beispiel ist hier libcxx:

// Definitions omitted 
T2 setiosflags (ios_base::fmtflags mask); 
T3 setbase(int base); 
template<charT> T4 setfill(charT c); 
T5 setprecision(int n); 
T6 setw(int n); 

T5, T6 usw. beziehen sich auf __imo_t4 usw., eine interne Struktur, die die eigentliche Arbeit des Manipulators der Fall ist. Da sie alle unterschiedliche Rückgabetypen haben, haben die Funktionen unterschiedliche Typen. Daher werden sie alle anders sein.

+2

Sie können nicht spezifizierte Typen verwenden.Ein gutes Beispiel dafür ist der Rückgabetyp von 'std :: bind'. –

+0

@GuillaumeRacicot Wie steht' std :: bind' zum Vergleich von 'std :: setw' zu' std :: setprecision'? – user6323422

+0

Die Downvotes sind ziemlich traurig. [Diese Antwort] (http://stackoverflow.com/a/37177827/6323422) kompiliert nicht und [diese Antwort] (http://stackoverflow.com/a/37178008/6323422) adressiert nicht den General Fall. – user6323422

2

können Sie SFINAE verwenden, indem Sie den Rückgabetyp der Funktion std::setprecision vergleicht. Da der Rückgabetyp nicht angegeben ist, benötigen Sie decltype und std::declval.

Hier ist die Lösung:

template <typename T, typename std::enable_if<std::is_same<decltype(std::setprecision(std::declval<int>())), typename std::decay<T>::type>::value, int>::type = 0> 
void foo(T&& t) { 
    cout << "enabled" << std::endl; 
} 

Hier änderte ich T& zu T&& so akzeptieren sie jede Art von Referenz. Das entfernen Sie die Verwendung Ihrer foohelper.

Darüber hinaus ist die Art Vergleich, zerfallen ich die Art T, da der Vergleich funktioniert nicht, wenn eine des Typs ein Referenztyp ist, weil T& nicht die gleiche wie T ist. Die Verwendung von std::decay wird die Referenz effektiv entfernen.

Beachten Sie, dass ich das SFINAE-Konstrukt innerhalb der Template-Deklaration verschoben habe. Dies dient dazu, SFINAE vom Rückgabetyp fernzuhalten. Die Verwendung von nicht typisierten Vorlagenparametern auf 0 hat keinen Einfluss auf die Verwendung der Funktion und stellt sicher, dass niemand die SFINAE-Prüfung effektiv umgehen kann.

Edit:

Sie können std::declval<int>() mit 0 in Ihrem Fall ersetzen. Ich wollte nur zeigen, wie Sie mit diesem Werkzeug den tatsächlichen Wert ersetzen können. Es gibt viele Fälle, in denen Sie keine Werte haben können.

+0

Vielen Dank. Aber ich muss Foohelper benutzen, so ist meine Anwendung geschrieben. – AAA

Verwandte Themen