Ich möchte mehr über die Meta-Programmierung von Vorlagen erfahren. Ich weiß, dass SFINAE für "Substitutionsfehler ist kein Fehler" steht. Aber kann mir jemand eine gute Verwendung für SFINAE zeigen?
c++
templates
metaprogramming
sfinae
rlbond
quelle
quelle
Antworten:
Hier ist ein Beispiel ( von hier ):
Wenn
IsClassT<int>::Yes
ausgewertet wird, kann 0 nicht konvertiert werden,int int::*
da int keine Klasse ist und daher keinen Elementzeiger haben kann. Wenn SFINAE nicht vorhanden wäre, würde ein Compilerfehler angezeigt, z. B. "0 kann nicht in Mitgliedszeiger für Nicht-Klassentyp int konvertiert werden". Stattdessen wird nur das...
Formular verwendet, das Two zurückgibt und daher als false ausgewertet wird. Int ist kein Klassentyp.quelle
...
, sondern dasint C::*
, was ich noch nie gesehen hatte und nachschlagen musste. Hier finden Sie die Antwort darauf, was das ist und wofür es verwendet werden könnte: stackoverflow.com/questions/670734/…Ich benutze gerne
SFINAE
, um boolesche Bedingungen zu überprüfen.Es kann sehr nützlich sein. Zum Beispiel habe ich damit überprüft, ob eine mit Operator Komma gesammelte Initialisiererliste nicht länger als eine feste Größe ist
Die Liste wird nur akzeptiert, wenn M kleiner als N ist, was bedeutet, dass die Initialisierungsliste nicht zu viele Elemente enthält.
Die Syntax
char(*)[C]
bedeutet: Zeiger auf ein Array mit dem Elementtyp char und sizeC
. WennC
false (hier 0) ist, erhalten wir den ungültigenchar(*)[0]
Zeiger auf ein Array mit der Größe Null: SFINAE macht es so, dass die Vorlage dann ignoriert wird.Ausgedrückt mit
boost::enable_if
, das sieht so ausIn der Praxis finde ich die Fähigkeit, Bedingungen zu überprüfen, oft eine nützliche Fähigkeit.
quelle
M <= N ? 1 : -1
könnte es vielleicht stattdessen funktionieren.int foo[0]
. Ich bin nicht überrascht, dass es unterstützt wird, da es den sehr nützlichen Trick "Struktur endet mit einem Array mit 0 Längen" ermöglicht ( gcc.gnu.org/onlinedocs/gcc/Zero-Length.html ).error C2466: cannot allocate an array of constant size 0
In C ++ 11 sind SFINAE-Tests viel schöner geworden. Hier sind einige Beispiele für häufige Verwendungen:
Wählen Sie abhängig von den Merkmalen eine Funktionsüberladung
Mit einem so genannten Typ-Sink-Idiom können Sie ziemlich willkürliche Tests für einen Typ durchführen, z. B. prüfen, ob er ein Mitglied hat und ob dieses Mitglied von einem bestimmten Typ ist
Hier ist ein Live-Beispiel: http://ideone.com/dHhyHE Ich habe kürzlich in meinem Blog einen ganzen Abschnitt über SFINAE und Tag-Versand geschrieben (schamloser Plug, aber relevant). Http://metaporky.blogspot.de/2014/08/ part-7-static-dispatch-function.html
Beachten Sie, dass es ab C ++ 14 ein std :: void_t gibt, das im Wesentlichen mit meinem TypeSink hier identisch ist.
quelle
TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>
an einem Ort und dannTypeSinkT<decltype(&T::bar)>
an einem anderen verwenden? Auch ist das&
notwendig instd::declval<T&>
?TypeSink
, C ++ 17 habenstd::void_t
:)Die enable_if- Bibliothek von Boost bietet eine schöne, saubere Oberfläche für die Verwendung von SFINAE. Eines meiner bevorzugten Anwendungsbeispiele befindet sich in der Boost.Iterator- Bibliothek. SFINAE wird verwendet, um Konvertierungen vom Iteratortyp zu aktivieren.
quelle
C ++ 17 bietet wahrscheinlich ein generisches Mittel zum Abfragen von Features. Siehe N4502 für Details, aber als eigenständiges Beispiel betrachten Sie Folgendes.
Dieser Teil ist der konstante Teil, setzen Sie ihn in eine Kopfzeile.
Das folgende Beispiel aus N4502 zeigt die Verwendung:
Im Vergleich zu den anderen Implementierungen ist diese ziemlich einfach: Ein reduzierter Satz von Werkzeugen (
void_t
unddetect
) reicht aus. Außerdem wurde berichtet (siehe N4502 ), dass es messbar effizienter ist (Kompilierungszeit und Compiler-Speicherverbrauch) als frühere Ansätze.Hier ist ein Live-Beispiel , das Portabilitätsverbesserungen für GCC vor 5.1 enthält.
quelle
Hier ist ein weiteres (spätes) SFINAE- Beispiel, das auf Greg Rogers ' Antwort basiert :
Auf diese Weise können Sie den
value
Wert des 's überprüfen , um festzustellen, obT
es sich um eine Klasse handelt oder nicht:quelle
int C::*
in Ihrer Antwort? Wie kannC::*
ein Parametername sein?int C::*
ist der Typ eines Zeigers auf eineint
Mitgliedsvariable vonC
.Hier ist ein guter Artikel von SFINAE: Eine Einführung in das SFINAE-Konzept von C ++: Introspektion eines Klassenmitglieds zur Kompilierungszeit .
Fassen Sie es wie folgt zusammen:
declval
ist ein Dienstprogramm, mit dem Sie einen "falschen Verweis" auf ein Objekt eines Typs erhalten, der nicht einfach zu erstellen ist.declval
ist sehr praktisch für unsere SFINAE-Konstruktionen.quelle
Hier verwende ich die Überladung von Vorlagenfunktionen (nicht direkt SFINAE), um zu bestimmen, ob ein Zeiger eine Funktion oder ein Elementklassenzeiger ist: ( Ist es möglich, die iostream cout / cerr-Elementfunktionszeiger zu korrigieren, die als 1 oder true gedruckt werden? )
https://godbolt.org/z/c2NmzR
Druckt
So wie der Code ist, könnte er (abhängig vom "guten" Willen des Compilers) einen Laufzeitaufruf für eine Funktion erzeugen, die true oder false zurückgibt. Wenn Sie die
is_function_pointer(var)
Auswertung beim Kompilieren erzwingen möchten (zur Laufzeit werden keine Funktionsaufrufe ausgeführt), können Sie denconstexpr
Variablentrick verwenden:Nach dem C ++ - Standard wird
constexpr
garantiert, dass alle Variablen zur Kompilierungszeit ausgewertet werden ( Berechnen der Länge eines C-Strings zur Kompilierungszeit. Ist dies wirklich ein Nachteil? ).quelle
Der folgende Code verwendet SFINAE, damit der Compiler eine Überladung basierend darauf auswählen kann, ob ein Typ eine bestimmte Methode hat oder nicht:
Ausgabe:
quelle