Scott Meyers veröffentlichte Inhalt und Status seines nächsten Buches EC ++ 11. Er schrieb, dass ein Punkt im Buch "Vermeiden Sie std::enable_if
in Funktionssignaturen" sein könnte .
std::enable_if
kann als Funktionsargument, als Rückgabetyp oder als Klassenvorlage oder Funktionsvorlagenparameter verwendet werden, um Funktionen oder Klassen bedingt aus der Überlastungsauflösung zu entfernen.
In dieser Frage werden alle drei Lösungen gezeigt.
Als Funktionsparameter:
template<typename T>
struct Check1
{
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, int>::value >::type* = 0) { return 42; }
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, double>::value >::type* = 0) { return 3.14; }
};
Als Vorlagenparameter:
template<typename T>
struct Check2
{
template<typename U = T, typename std::enable_if<
std::is_same<U, int>::value, int>::type = 0>
U read() { return 42; }
template<typename U = T, typename std::enable_if<
std::is_same<U, double>::value, int>::type = 0>
U read() { return 3.14; }
};
Als Rückgabetyp:
template<typename T>
struct Check3
{
template<typename U = T>
typename std::enable_if<std::is_same<U, int>::value, U>::type read() {
return 42;
}
template<typename U = T>
typename std::enable_if<std::is_same<U, double>::value, U>::type read() {
return 3.14;
}
};
- Welche Lösung sollte bevorzugt werden und warum sollte ich andere vermeiden?
- In welchen Fällen betrifft "
std::enable_if
In Funktionssignaturen vermeiden " die Verwendung als Rückgabetyp (der nicht Teil der normalen Funktionssignatur, sondern der Vorlagenspezialisierungen ist)? - Gibt es Unterschiede zwischen Funktionsvorlagen für Mitglieder und Nichtmitglieder?
std::enable_if
, meine Funktionssignaturen (insbesondere die hässlichenullptr
Version mit zusätzlichen Funktionsargumenten) nicht gerne überladen möchte , weil es immer so aussieht, wie es ist, ein seltsamer Hack (für etwas, dasstatic if
vielleicht etwas ist) viel schöner und sauberer machen) mit Vorlage Black-Magic, um eine interessante Sprachfunktion auszunutzen. Aus diesem Grund bevorzuge ich das Versenden von Tags, wann immer dies möglich ist (nun, Sie haben immer noch zusätzliche seltsame Argumente, aber nicht in der öffentlichen Oberfläche und auch viel weniger hässlich und kryptisch ).=0
intypename std::enable_if<std::is_same<U, int>::value, int>::type = 0
erreichen? Ich konnte keine richtigen Ressourcen finden, um es zu verstehen. Ich weiß, dass der erste Teil zuvor=0
einen Mitgliedstyp hat,int
wennU
undint
der gleiche ist. Danke vielmals!Antworten:
Setzen Sie den Hack in die Vorlagenparameter .
Der
enable_if
On-Template-Parameter-Ansatz hat mindestens zwei Vorteile gegenüber den anderen:Lesbarkeit : Die Typen enable_if use und return / argument werden nicht zu einem unordentlichen Teil von Typnamen-Disambiguatoren und verschachtelten Typzugriffen zusammengeführt. Auch wenn die Unordnung des Disambiguators und des verschachtelten Typs mit Alias-Vorlagen verringert werden kann, würde dies dennoch zwei nicht miteinander verbundene Dinge zusammenführen. Die Verwendung von enable_if bezieht sich auf die Vorlagenparameter und nicht auf die Rückgabetypen. Wenn sie in den Vorlagenparametern enthalten sind, sind sie näher an dem, was wichtig ist.
Universelle Anwendbarkeit : Konstruktoren haben keine Rückgabetypen, und einige Operatoren können keine zusätzlichen Argumente haben, sodass keine der beiden anderen Optionen überall angewendet werden kann. Das Einfügen von enable_if in einen Vorlagenparameter funktioniert überall, da Sie SFINAE ohnehin nur für Vorlagen verwenden können.
Für mich ist der Lesbarkeitsaspekt der große Motivationsfaktor bei dieser Wahl.
quelle
FUNCTION_REQUIRES
Makros hier erleichtert das Lesen erheblich, funktioniert auch in C ++ 03-Compilern und basiert auf der Verwendungenable_if
im Rückgabetyp. Die Verwendungenable_if
von Funktionsvorlagenparametern führt außerdem zu Problemen beim Überladen, da die Funktionssignatur jetzt nicht eindeutig ist und mehrdeutige Überladungsfehler verursacht.enable_if
einen standardmäßigen Nicht-Typ-Vorlagenparameter zu verwenden, der eine Überladung ermöglicht. Dhenable_if_t<condition, int> = 0
statttypename = enable_if_t<condition>
.flamingdangerzone
Link in Ihrem Kommentar scheint jetzt zu einer Seite zu führen, auf der Spyware installiert wird. Ich habe es für die Aufmerksamkeit des Moderators markiert.std::enable_if
stützt sich beim Abzug von Vorlagenargumenten auf das Prinzip " Substitionsfehler ist kein Fehler " (auch bekannt als SFINAE) . Dies ist eine sehr fragile Sprachfunktion, und Sie müssen sehr vorsichtig sein, um sie richtig zu machen.enable_if
eine verschachtelte Vorlage oder Typdefinition enthält (Hinweis:::
Suchen Sie nach Token), ist die Auflösung dieser verschachtelten Vorlagen oder Typen normalerweise ein nicht abgeleiteter Kontext . Jeder Substitutionsfehler in einem solchen nicht abgeleiteten Kontext ist ein Fehler .enable_if
Überladungen können keine Überlappung aufweisen, da die Überlastungsauflösung nicht eindeutig wäre. Dies ist etwas, das Sie als Autor selbst überprüfen müssen, obwohl Sie gute Compiler-Warnungen erhalten würden.enable_if
manipuliert den Satz funktionsfähiger Funktionen während der Überlastungsauflösung, die überraschende Wechselwirkungen haben können, abhängig vom Vorhandensein anderer Funktionen, die aus anderen Bereichen (z. B. über ADL) eingebracht werden. Dies macht es nicht sehr robust.Kurz gesagt, wenn es funktioniert, funktioniert es, aber wenn es nicht funktioniert, kann es sehr schwer zu debuggen sein. Eine sehr gute Alternative ist die Verwendung des Tag-Dispatchings , dh das Delegieren an eine Implementierungsfunktion (normalerweise in einem
detail
Namespace oder in einer Hilfsklasse), die ein Dummy-Argument empfängt, das auf derselben Bedingung zur Kompilierungszeit basiert, die Sie in der verwendenenable_if
.Das Tag-Dispatching manipuliert den Überladungssatz nicht, hilft Ihnen jedoch bei der Auswahl genau der gewünschten Funktion, indem die richtigen Argumente über einen Ausdruck zur Kompilierungszeit (z. B. in einem Typmerkmal) bereitgestellt werden. Nach meiner Erfahrung ist dies viel einfacher zu debuggen und richtig zu machen. Wenn Sie ein aufstrebender Bibliotheksschreiber mit ausgefeilten Typmerkmalen sind, benötigen Sie möglicherweise etwas
enable_if
, aber für die meisten regelmäßigen Anwendungen zur Kompilierungszeit wird dies nicht empfohlen.quelle
enable_if
es richtig gemacht werden würde?is_f_able
ist etwas, das ich als eine Aufgabe für Bibliotheksschreiber betrachte, die natürlich SFINAE verwenden können, wenn dies ihnen einen Vorteil verschafft, aber für "normale" Benutzer und mit einem bestimmten Merkmalis_f_able
denke ich, dass das Versenden von Tags einfacher ist.Der Vorlagenparameter
Es kann leicht falsch verwendet werden und führt zu Fehlern bei Überlastungen:
Beachten Sie
typename = std::enable_if_t<cond>
statt richtigstd::enable_if_t<cond, int>::type = 0
Rückgabetyp:
Zuletzt im Funktionsparameter:
+
,-
,*
, ...)void* = nullptr
) (der Funktionszeiger würde sich also unterscheiden und so weiter).Es gibt subtile Unterschiede bei der Vererbung und
using
:Nach dem
using-declarator
(Schwerpunkt Mine):namespace.udecl
Sowohl für das Vorlagenargument als auch für den Rückgabetyp sind die folgenden Methoden ausgeblendet:
Demo (gcc findet fälschlicherweise die Basisfunktion).
Während mit Argument ein ähnliches Szenario funktioniert:
Demo
quelle