Ich versuche, ein einfaches Beispiel zum Arbeiten zu bringen, um zu verstehen, wie man es benutzt std::enable_if
. Nachdem ich diese Antwort gelesen hatte , dachte ich, es sollte nicht zu schwierig sein, ein einfaches Beispiel zu finden. Ich möchte verwenden std::enable_if
, um zwischen zwei Elementfunktionen zu wählen und nur eine davon zuzulassen.
Leider lässt sich das Folgende nicht mit gcc 4.7 kompilieren und nach stundenlangen Versuchen frage ich euch, was mein Fehler ist.
#include <utility>
#include <iostream>
template< class T >
class Y {
public:
template < typename = typename std::enable_if< true >::type >
T foo() {
return 10;
}
template < typename = typename std::enable_if< false >::type >
T foo() {
return 10;
}
};
int main() {
Y< double > y;
std::cout << y.foo() << std::endl;
}
gcc meldet folgende Probleme:
% LANG=C make CXXFLAGS="-std=c++0x" enable_if
g++ -std=c++0x enable_if.cpp -o enable_if
enable_if.cpp:12:65: error: `type' in `struct std::enable_if<false>' does not name a type
enable_if.cpp:13:15: error: `template<class T> template<class> T Y::foo()' cannot be overloaded
enable_if.cpp:9:15: error: with `template<class T> template<class> T Y::foo()'
Warum löscht g ++ nicht die falsche Instanziierung für die Funktion des zweiten Mitglieds? std::enable_if< bool, T = void >::type
Existiert standardmäßig nur, wenn der boolesche Template-Parameter true ist. Aber warum betrachtet g ++ dies nicht als SFINAE? Ich denke, dass die Überladungsfehlermeldung von dem Problem herrührt, dass g ++ die zweite Mitgliedsfunktion nicht löscht und glaubt, dass dies eine Überladung sein sollte.
std::is_same< T, int >::value
und! std::is_same< T, int >::value
das ergibt das gleiche Ergebnis.Antworten:
SFINAE funktioniert nur, wenn durch Ersetzen eines Vorlagenarguments durch Argumentation das Konstrukt schlecht geformt wird. Es gibt keine solche Substitution.
Dies liegt daran, dass beim Instanziieren der Klassenvorlage (was unter anderem beim Erstellen eines Objekts vom Typ geschieht
Y<int>
) alle Elementdeklarationen instanziiert werden (nicht unbedingt deren Definitionen / Körper!). Darunter befinden sich auch die Mitgliedsvorlagen. Beachten Sie, dass diesT
dann bekannt ist und!std::is_same< T, int >::value
false ergibt. Es wird also eine Klasse erstellt,Y<int>
die enthältDer
std::enable_if<false>::type
greift auf einen nicht existierenden Typ zu, so dass die Deklaration fehlerhaft ist. Und somit ist Ihr Programm ungültig.Sie müssen dafür sorgen, dass die Mitgliedsvorlagen
enable_if
von einem Parameter der Mitgliedsvorlage selbst abhängen. Dann sind die Deklarationen gültig, da der gesamte Typ noch abhängig ist. Wenn Sie versuchen, einen von ihnen aufzurufen, erfolgt die Argumentableitung für ihre Vorlagenargumente und SFINAE wie erwartet. Siehe diese Frage und die entsprechende Antwort dazu.quelle
Y
Vorlagenklasse instanziiert wird, kompiliert der Compiler die Funktionen der Vorlagenelemente nicht. Der Compiler führt jedoch die ErsetzungT
in die Elementvorlage DECLARATIONS durch, damit diese Elementvorlagen zu einem späteren Zeitpunkt instanziiert werden können. Dieser Fehlerpunkt ist nicht SFINAE, da SFINAE nur beim Bestimmen des Satzes möglicher Funktionen für die Überlastauflösung gilt und das Instanziieren einer Klasse nicht das Bestimmen eines Satzes von Funktionen für die Überlastauflösung ist. (Zumindest denke ich!)Ich habe dieses kurze Beispiel gemacht, das auch funktioniert.
Kommentar, wenn Sie möchten, dass ich näher darauf eingehe. Ich denke, der Code ist mehr oder weniger selbsterklärend, aber andererseits habe ich ihn so gemacht, dass ich mich möglicherweise irre :)
Sie können es hier in Aktion sehen .
quelle
error C4519: default template arguments are only allowed on a class template
.Q
, obwohl diese gleich istT
?test
Mitgliedsfunktion vorlegen müssen. Beide können nicht gleichzeitig existieren.Q
Leitet einfach den Klassenvorlagentyp weiterT
. Sie könnten die KlassenvorlageT
wie folgt entfernen : cpp.sh/4nxw, aber das macht den Zweck irgendwie zunichte .Für diejenigen, die nach einer Lösung suchen, die "einfach funktioniert":
Kompilieren mit:
Laufen gibt:
quelle
std::enable_if_t
zuresolvedType
.Aus diesem Beitrag:
Aber so etwas kann man machen:
quelle
Eine Möglichkeit, dieses Problem zu lösen, besteht darin, die Spezialisierung in eine andere Klasse zu verschieben und dann von dieser Klasse zu erben. Möglicherweise müssen Sie die Reihenfolge der Vererbung ändern, um Zugriff auf alle anderen zugrunde liegenden Daten zu erhalten. Diese Technik funktioniert jedoch.
Der Nachteil dieser Technik besteht darin, dass Sie, wenn Sie viele verschiedene Dinge für verschiedene Elementfunktionen testen müssen, für jede eine Klasse erstellen und diese im Vererbungsbaum verketten müssen. Dies gilt für den Zugriff auf allgemeine Datenelemente.
Ex:
quelle
Der Boolesche Wert muss vom abgeleiteten Vorlagenparameter abhängen. Eine einfache Möglichkeit zur Behebung besteht darin, einen booleschen Standardparameter zu verwenden:
Dies funktioniert jedoch nicht, wenn Sie die Member-Funktion überladen möchten. Verwenden Sie stattdessen am besten
TICK_MEMBER_REQUIRES
die Tick- Bibliothek:Sie können auch implementieren, dass Ihr eigenes Mitglied ein solches Makro benötigt (nur für den Fall, dass Sie keine andere Bibliothek verwenden möchten):
quelle
Hier ist mein minimalistisches Beispiel mit einem Makro. Verwenden Sie doppelte Klammern,
enable_if((...))
wenn Sie komplexere Ausdrücke verwenden.quelle