SFINAE
Funktionen bedingt aktivieren
SFINAE ist eine kostenlose C++ Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C++ Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C++ Academy-Kurs umfasst insgesamt 4 Lektionen.
Was bedeutet SFINAE?
SFINAE steht für "Substitution Failure Is Not An Error". Wenn der Compiler Templateargumente einsetzt und das Ergebnis nicht wohlgeformt ist, wird dieser Kandidat einfach entfernt, anstatt einen schwerwiegenden Fehler zu verursachen.
- Damit lassen sich bedingte Überladungen ermöglichen.
- Es bildet die Grundlage der Auswahl zur Compilezeit.
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
isOdd(T n) { return n % 2 != 0; }
int main() {
std::cout << std::boolalpha << isOdd(3) << '\n';
return 0;
}Substitutionsfehler in Aktion
Wenn das Einsetzen eines Typs die Signatur einer Funktion ungültig macht, überspringt der Compiler sie stillschweigend und prüft weitere Kandidaten.
#include <iostream>
template <typename T>
auto sizeOfValue(T t) -> decltype(t.size()) { return t.size(); }
int main() {
std::string s = "hello";
std::cout << sizeOfValue(s) << '\n';
return 0;
}Zwei Überladungen, eine gewinnt
Stellen Sie zwei Überladungen bereit, von denen für einen bestimmten Typ genau eine erfolgreich substituiert wird. SFINAE verwirft die ungültige Überladung.
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_integral<T>::value, const char*>::type
name() { return "integral"; }
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, const char*>::type
name() { return "floating"; }
int main() {
std::cout << name<int>() << ' ' << name<double>() << '\n';
return 0;
}decltype und Expression-SFINAE
Wenn Sie decltype auf einen Ausdruck im Rückgabetyp anwenden, ist eine Funktion nur dann vorhanden, wenn dieser Ausdruck für den Typ gültig ist.
#include <iostream>
template <typename T>
auto tryDouble(T x) -> decltype(x + x) { return x + x; }
int main() {
std::cout << tryDouble(21) << '\n';
std::cout << tryDouble(2.5) << '\n';
return 0;
}Ein Member erkennen
Ein klassischer SFINAE-Trick erkennt, ob ein Typ ein bestimmtes Member besitzt, indem geprüft wird, ob ein Ausdruck, der dieses Member verwendet, kompiliert werden kann.
#include <iostream>
#include <type_traits>
#include <vector>
template <typename T>
auto hasSize(int) -> decltype(std::declval<T>().size(), std::true_type{});
template <typename T>
std::false_type hasSize(...);
int main() {
std::cout << std::boolalpha;
std::cout << decltype(hasSize<std::vector<int>>(0))::value << '\n';
std::cout << decltype(hasSize<int>(0))::value << '\n';
return 0;
}Der ...-Fallback
Eine Überladung, die ... (variable Argumente) entgegennimmt, hat die niedrigste Priorität. Sie dient als Auffanglösung, wenn die durch SFINAE eingeschränkte Überladung fehlschlägt.
#include <iostream>
template <typename T>
auto pick(int) -> decltype(T{}.value, void()) { std::cout << "has value\n"; }
template <typename T>
void pick(...) { std::cout << "no value\n"; }
struct WithValue { int value = 0; };
int main() {
pick<WithValue>(0);
pick<int>(0);
return 0;
}void_t
std::void_t aus C++17 vereinfacht die Erkennung: Es ordnet alle wohlgeformten Typen void zu, sodass eine Spezialisierung nur dann greift, wenn der geprüfte Ausdruck gültig ist.
#include <iostream>
#include <type_traits>
#include <vector>
template <typename T, typename = void>
struct HasSize : std::false_type {};
template <typename T>
struct HasSize<T, std::void_t<decltype(std::declval<T>().size())>> : std::true_type {};
int main() {
std::cout << std::boolalpha;
std::cout << HasSize<std::vector<int>>::value << '\n';
std::cout << HasSize<int>::value << '\n';
return 0;
}Warum nicht einfach einen Fehler ausgeben?
Ohne SFINAE wäre eine ungültige Substitution ein schwerwiegender Kompilierungsfehler, von dem sich nicht wieder zurückkehren ließe. SFINAE macht daraus stillschweigend "kein Kandidat".
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, bool>::type
isNull(T p) { return p == nullptr; }
int main() {
int x = 0;
std::cout << std::boolalpha << isNull(&x) << '\n';
return 0;
}Alternative: Tag Dispatch
SFINAE kann schwer lesbar sein. Beim Tag Dispatch wird mithilfe kleiner Tagtypen und der Überladungsauflösung eine Implementierung ausgewählt.
#include <iostream>
#include <type_traits>
template <typename T>
void impl(T v, std::true_type) { std::cout << "integral: " << v << '\n'; }
template <typename T>
void impl(T v, std::false_type) { std::cout << "other: " << v << '\n'; }
template <typename T>
void handle(T v) { impl(v, std::is_integral<T>{}); }
int main() {
handle(5);
handle(2.5);
return 0;
}SFINAE und constexpr if
In C++17 ersetzt if constexpr SFINAE häufig bei Verzweigungen innerhalb einer einzelnen Funktion, da nicht verwendete Zweige zur Compilezeit verworfen werden.
#include <iostream>
#include <type_traits>
template <typename T>
void describe(T v) {
if constexpr (std::is_integral<T>::value) {
std::cout << "integral: " << v << '\n';
} else {
std::cout << "other: " << v << '\n';
}
}
int main() {
describe(7);
describe(3.14);
return 0;
}Alles zusammenführen
SFINAE spielt seine Stärken aus, wenn Sie auf Ebene der Überladungen zwischen separaten Funktionstemplates anhand von Typeigenschaften auswählen müssen.
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_signed<T>::value, T>::type
absValue(T v) { return v < 0 ? -v : v; }
int main() {
std::cout << absValue(-9) << '\n';
return 0;
}Schnelltest
Testen Sie Ihr Verständnis von SFINAE.
Zusammenfassung
Sie haben SFINAE kennengelernt:
- Eine ungültige Substitution entfernt einen Kandidaten, anstatt einen Fehler zu verursachen
- Es wird mit
enable_if,decltypeundvoid_tverwendet, um Funktionen bedingt zu aktivieren - Eine
...-Überladung dient als Fallback - Zu den modernen Alternativen gehören Tag Dispatch und
if constexpr
Als Nächstes beschäftigen Sie sich ausführlich mit den klassischen enable_if-Mustern.
Häufig gestellte Fragen
Ist die Lektion „SFINAE“ kostenlos?
Ja — der vollständige Text von „SFINAE“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C++ Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C++ Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „SFINAE“?
Funktionen bedingt aktivieren Du übst C++ Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um C++ Academy zu starten?
Keine Vorkenntnisse erforderlich. C++ Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.
Wie lange dauert die Lektion „SFINAE“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser C++ Academy-Lektion Code schreiben und ausführen?
Ja. Jede C++ Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.