enable_if
-Klasse
Wandelt einen Typ für die SFINAE-Überladungsauflösung bedingt in eine Instanz um. Der geschachtelte typedef-enable_if<Condition,Type>::type
existiert und ist ein Synonym für Type
– wenn und nur dann, wenn Condition
gleich true
ist.
Syntax
template <bool B, class T = void>
struct enable_if;
Parameter
B
Der Wert, der das Vorhandensein des Ergebnistyps bestimmt.
T
Der Typ, der instanziiert werden soll, wenn B
dies der Typ ist true
.
Hinweise
Ist B
dies true
der Name , enable_if<B, T>
hat einen geschachtelten Typedef mit dem Namen "type", der ein Synonym für T
.
Ist B
dies der Grund, enable_if<B, T>
weist false
keinen geschachtelten Typedef mit dem Namen "type" auf.
Die folgende Alias-Vorlage steht zur Verfügung:
template <bool B, class T = void>
using enable_if_t = typename enable_if<B,T>::type;
In C++ ist der Ersetzungsfehler von Vorlagenparametern kein Fehler selbst – dies wird als SFINAE bezeichnet (Ersetzungsfehler ist kein Fehler). Normalerweise wird enable_if
verwendet, um Kandidaten aus einer Überladungsauflösung zu entfernen – also aus dem Überladungssatz auszusortieren, sodass eine Definition zugunsten einer anderen zurückgewiesen werden kann. Dies entspricht dem SFINAE-Verhalten. Weitere Informationen zu SFINAE finden Sie unter Ersetzungsfehler ist kein Fehler in Wikipedia.
Im Folgenden finden Sie vier Beispielszenarien:
- Szenario 1: Umschließen des Rückgabetyps einer Funktion:
template <your_stuff>
typename enable_if<your_condition, your_return_type>::type
yourfunction(args) {// ...
}
// The alias template makes it more concise:
template <your_stuff>
enable_if_t<your_condition, your_return_type>
yourfunction(args) {// ...
}
- Szenario 2: Hinzufügen eines Funktionsparameters mit einem Standardargument:
template <your_stuff>
your_return_type_if_present
yourfunction(args, enable_if_t<your condition, FOO> = BAR) {// ...
}
- Szenario 3: Hinzufügen eines Vorlagenparameters mit einem Standardargument:
template <your_stuff, typename Dummy = enable_if_t<your_condition>>
rest_of_function_declaration_goes_here
- Szenario 4: Wenn Ihre Funktion über ein Argument ohne Vorlage verfügt, können Sie ihren Typ umschließen:
template <typename T>
void your_function(const T& t,
enable_if_t<is_something<T>::value, const string&>
s) {// ...
}
Szenario 1 funktioniert nicht mit Konstruktoren und Konvertierungsoperatoren, da diese keine Rückgabetypen haben.
In Szenario 2 bleibt der Parameter unbenannt. Sie können ihn ::type Dummy = BAR
nennen, aber der Namens-Dummy
ist irrelevant, und eine Benennung löst wahrscheinlich eine Warnung über einen nicht referenzierten Parameter aus. Sie müssen einen FOO
Funktionsparameter typ und ein BAR
-Standardargument auswählen. Sie können diese int
und 0
nennen, aber in diesem Falle könnten Benutzer Ihres Codes zufällig eine zusätzliche Ganzzahl an diese Funktion übergeben, die ignoriert würde. Wir empfehlen stattdessen, void **
und entweder 0
oder nullptr
zu verwenden, da fast nichts in void **
umwandelbar ist:
template <your_stuff>
your_return_type_if_present
yourfunction(args, typename enable_if<your_condition, void **>::type = nullptr) {// ...
}
Szenario 2 funktioniert auch mit normalen Konstruktoren. Allerdings funktioniert es nicht mit Konvertierungsoperatoren, da diese keine zusätzlichen Parameter aufnehmen können. Es funktioniert auch nicht für variadic
Konstruktoren, da das Hinzufügen zusätzlicher Parameter das Funktionsparameterpaket zu einem nicht abgeleiteten Kontext macht und dadurch den Zweck enable_if
von .
Szenario 3 verwendet den Namen Dummy
, aber dieser ist optional. Nur „typename = typename
“ wäre geeignet, aber wenn Ihnen dies seltsam vorkommt, können Sie einen „Dummy“-Namen wählen. Wählen Sie jedoch keinen Namen, der auch in der Funktionsdefinition verwendet werden könnte. Wenn Sie keinen Typ für das enable_if
angeben, bleibt es standardmäßig leer, was verständlich ist, da es Sie nicht kümmern muss, was Dummy
ist. Dies funktioniert für alles, einschließlich Konvertierungsoperatoren und variadic
Konstruktoren.
Szenario 4 funktioniert mit Konstruktoren, die keine Rückgabetypen haben, und löst dadurch die Umschließungseinschränkung von Szenario 1 auf. Szenario 4 ist allerdings auf Funktionsargumente ohne Vorlage beschränkt, die nicht immer zur Verfügung stehen. (Die Verwendung von Szenario 4 mit einem Funktionsargument mit Vorlage verhindert, dass die Ableitung eines Vorlagenarguments in diesem Szenario funktioniert.)
enable_if
ist leistungsstark, aber bei falscher Verwendung auch gefährlich. Da es sein Zweck ist, Kandidaten vor einer Überladungsauflösung verschwinden zu lassen, wenn es fehlerhaft verwendet wird, können seine Wirkungen sehr verwirrend sein. Hier sind einige Empfehlungen dafür:
Verwenden
enable_if
Sie zum Kompilieren nicht die Auswahl zwischen Implementierungen. Schreiben Sie niemals einenable_if
fürCONDITION
und ein anderes für!CONDITION
. Verwenden Sie stattdessen ein tag dispatch-Muster – z.B. einen Algorithmus, der Implementierungen abhängig von den Stärken der Iteratoren auswählt, die sie erhalten.Verwenden
enable_if
Sie nicht, um Anforderungen zu erzwingen. Wenn Sie Vorlagenparameter überprüfen möchten und wenn die Überprüfung fehlschlägt, führen Sie einen Fehler aus, anstatt eine andere Implementierung auszuwählen.static_assert
Verwenden Sie
enable_if
, wenn Sie einen Überladungssatz haben, der einen ansonsten guten Code mehrdeutig macht. Dies tritt sehr häufig bei der impliziten Konvertierung von Konstruktoren auf.
Beispiel
In diesem Beispiel wird erläutert, wie die C++-Standardbibliotheksvorlagenfunktion std::make_pair()
die Vorteile von enable_if
.
void func(const pair<int, int>&);
void func(const pair<string, string>&);
func(make_pair("foo", "bar"));
In diesem Beispiel gibt make_pair("foo", "bar")
pair<const char *, const char *>
zurück. Überladungsauslösung, die bestimmen muss, welche func()
Sie möchten. pair<A, B>
hat einen impliziten Konvertierungskonstruktor aus pair<X, Y>
. Dies ist nicht neu, sondern war schon in C++98 vorhanden. In C++98/03 ist allerdings die Signatur des impliziten Konvertierungskonstruktors immer vorhanden, selbst wenn es pair<int, int>(const pair<const char *, const char *>&)
ist. Für die Überladungsauslösung ist es egal, wenn ein Versuch, diesen Konstruktor zu instanzieren, fehlschlägt, da const char *
nicht implizit in int
konvertierbar ist; sie schaut nur auf Signaturen, bevor Funktionsdefinitionen instanziert werden. Deshalb ist der Beispielcode mehrdeutig, da Signaturen zur Konvertierung von pair<const char *, const char *>
in pair<int, int>
und in pair<string, string>
vorhanden sind.
In C++11 wurde diese Mehrdeutigkeit durch Verwendung von enable_if
aufgelöst, um sicherzustellen, dass pair<A, B>(const pair<X, Y>&)
nur dann vorhanden ist, wenn const X&
implizit in A
konvertierbar ist und const Y&
implizit in B
konvertierbar ist. Dies ermöglicht die Überladungsauflösung, die pair<const char *, const char *>
nicht konvertierbar pair<int, int>
ist und dass die überlastete pair<string, string>
Überladung lebensfähig ist.
Anforderungen
Header: <type_traits>
Namespace:std