Substitution failure is not an errorSubstitution failure is not an error (SFINAE) ist eine Programmiertechnik in der Programmiersprache C++. Dabei wird die Tatsache ausgenutzt, dass eine fehlgeschlagene Substitution von Template-Argumenten keinen Kompilierfehler erzeugt. Ist beispielsweise eine Funktion mehrfach überladen und sind gewisse Kandidaten das Ergebnis der Instanziierung eines Funktionstemplates mit möglicherweise deduzierten Template-Argumenten, so wird – sofern die Substitution der Template-Argumente fehlgeschlagen ist – die Überladung aus der Menge der Kandidaten entfernt, ohne dass der Kompiliervorgang mit einem Kompilierfehler abgebrochen wird. Sind die schließlich verbliebenen Kandidaten mehrdeutig, beispielsweise weil sie dieselbe Signatur haben und sich nur durch den Rückgabewert unterscheiden, oder sind keine Kandidaten für den Funktionsaufruf mehr vorhanden, so wird dennoch wie üblich ein Kompilierfehler erzeugt. BeispieleAls Standardbeispiel sei ein Konstrukt genannt, das von einem Typen #include <iostream>
template<typename T> // Von T soll bestimmt werden, ob T::Type ein Typ ist
class HasType
{
// Es werden zwei Typen benötigt, die unterschiedlich groß sind, sodass man sie mit sizeof differenzieren kann:
typedef char FalseType[1];
typedef char TrueType[2];
// Es folgt die überladene Funktion, mittels welcher SFINAE angewendet wird:
template<typename U>
static TrueType& Tester(typename U::Type*);
template<typename>
static FalseType& Tester(...);
public:
// Die gesammelte Information wird mit einem einfachen booleschen Wert nach außen gegeben:
static const bool Value = sizeof(Tester<T>(0)) == sizeof(TrueType);
};
struct Foo
{
typedef int Type;
};
int main()
{
std::cout << std::boolalpha;
std::cout << HasType<int>::Value << '\n'; // Gibt false aus
std::cout << HasType<Foo>::Value << '\n'; // Gibt true aus
}
Die Rückgabewerte der Ein beliebter Einsatzort von SFINAE ist das Restringieren bzw. ein Spezialisieren auf Typen, die eine bestimmte Gemeinsamkeit teilen. Ein Beispiel: #include <iostream>
#include <type_traits>
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void f(T Value)
{
std::cout << "Int: " << Value << '\n';
}
template<typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
void f(T Value)
{
std::cout << "Float: " << Value << '\n';
}
template<typename T, typename std::enable_if<std::is_pointer<T>::value, int>::type = 0>
void f(T Value)
{
std::cout << "Pointer: " << Value << '\n';
}
int main()
{
int n = 42;
f(n); // Int: 42
f(2.7); // Float: 2.7
f(&n); // Pointer: 002DFA14
}
Wegen der Default-Template-Argumente ist das Programm nicht in C++03 lauffähig, sondern erst in C++11. Die Elemente Dank SFINAE spart man sich hier das separate Spezialisieren des Funktionstemplates auf alle einzelnen integralen bzw. Fließkommatypen. Weblinks |