„Substitution failure is not an error“ – Versionsunterschied
[ungesichtete Version] | [gesichtete Version] |
Fytcha (Diskussion | Beiträge) K Asfdlol verschob die Seite Benutzer:Asfdlol/Substitution failure is not an error nach Substitution failure is not an error: Artikel fertig gestellt |
→Beispiele: Typo; Interpunktion; Grammatik |
||
Zeile 4: | Zeile 4: | ||
== Beispiele == |
== Beispiele == |
||
Als Standardbeispiel sei ein Konstrukt genannt, |
Als Standardbeispiel sei ein Konstrukt genannt, das von einem Typen <code>T</code> bestimmt, ob <code>T::Type</code> ebenfalls ein Typ ist. |
||
<syntaxhighlight lang="cpp"> |
<syntaxhighlight lang="cpp"> |
||
#include <iostream> |
#include <iostream> |
||
Zeile 11: | Zeile 11: | ||
class HasType |
class HasType |
||
{ |
{ |
||
// Es werden zwei Typen benötigt, die unterschiedlich |
// Es werden zwei Typen benötigt, die unterschiedlich groß sind, sodass man sie mit sizeof differenzieren kann: |
||
typedef char FalseType[1]; |
typedef char FalseType[1]; |
||
typedef char TrueType[2]; |
typedef char TrueType[2]; |
||
Zeile 23: | Zeile 23: | ||
public: |
public: |
||
// Die gesammelte Information wird mit einem einfachen booleschen Wert nach |
// Die gesammelte Information wird mit einem einfachen booleschen Wert nach außen gegeben: |
||
static const bool Value = sizeof(Tester<T>(0)) == sizeof(TrueType); |
static const bool Value = sizeof(Tester<T>(0)) == sizeof(TrueType); |
||
}; |
}; |
||
Zeile 39: | Zeile 39: | ||
} |
} |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
Die Rückgabewerte der <code>Tester</code>-Funktion sind [[Referenz (Programmierung)|Referenzen]], da in C++ bekanntlich keine Arrays zurückgegeben werden können. Der <code>sizeof</code>-Operator gibt beim Anwenden auf eine Referenz die Größe des referenzierten Types zurück. Beim Anwenden auf einen vermeintlichen Funktionsaufruf (also auf eine Funktion inkl. Funktionsargumente) wird die Größe des Rückgabewertes angegeben. Da die Funktion in Tat und Wahrheit nie wirklich aufgerufen wird, ist keine Funktionsdefinition von Nöten. Um die Größe des Rückgabewertes angeben zu können, muss zuerst die korrekte Funktion ausgewählt werden. Wird als Template-Argument <code>T = Foo</code> gewählt, so sind beide Überladungen korrekt. In diesem Fall wird anhand der Regeln überladener Funktionen die näherliegende ausgewählt. Da die [[variadische Funktion]] keine [[Typsicherheit]] bietet, wird die erste Funktion (die die einen <code>TrueType</code> zurückgibt) bevorzugt. Wird hingegen <code>T = int</code> eingegeben, so wird die erste Funktion aus der Menge der infrage kommenden Überladungen ausgeschieden. Dies erzeugt dank SFINAE keinen Kompilierfehler. Es wird |
Die Rückgabewerte der <code>Tester</code>-Funktion sind [[Referenz (Programmierung)|Referenzen]], da in C++ bekanntlich keine Arrays zurückgegeben werden können. Der <code>sizeof</code>-Operator gibt beim Anwenden auf eine Referenz die Größe des referenzierten Types zurück. Beim Anwenden auf einen vermeintlichen Funktionsaufruf (also auf eine Funktion inkl. Funktionsargumente) wird die Größe des Rückgabewertes angegeben. Da die Funktion in Tat und Wahrheit nie wirklich aufgerufen wird, ist keine Funktionsdefinition von Nöten. Um die Größe des Rückgabewertes angeben zu können, muss zuerst die korrekte Funktion ausgewählt werden. Wird als Template-Argument <code>T = Foo</code> gewählt, so sind beide Überladungen korrekt. In diesem Fall wird anhand der Regeln überladener Funktionen die näherliegende ausgewählt. Da die [[variadische Funktion]] keine [[Typsicherheit]] bietet, wird die erste Funktion (die, die einen <code>TrueType</code> zurückgibt) bevorzugt. Wird hingegen <code>T = int</code> eingegeben, so wird die erste Funktion aus der Menge der infrage kommenden Überladungen ausgeschieden. Dies erzeugt dank SFINAE keinen Kompilierfehler. Es wird außerdem die Tatsache ausgenutzt, dass <code>int</code>s in C++ implizit in [[Zeiger (Informatik)|Zeiger]] konvertiert werden. |
||
Ein beliebter Einsatzort von SFINAE ist das Restringieren bzw. ein Spezialisieren auf Typen, die eine bestimmte Gemeinsamkeit teilen. Ein Beispiel: |
Ein beliebter Einsatzort von SFINAE ist das Restringieren bzw. ein Spezialisieren auf Typen, die eine bestimmte Gemeinsamkeit teilen. Ein Beispiel: |
||
Zeile 73: | Zeile 73: | ||
} |
} |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
Wegen |
Wegen der Default-Template-Argumente ist das Programm nicht in C++03 lauffähig, sondern erst in C++11. Die Elemente <code>std::enable_if</code> etc. hingegen sind auch in C++03 implementierbar. |
||
Dank SFINAE spart man sich hier das separate Spezialisieren des Funktionstemplates auf alle einzelnen [[Integer (Datentyp)|integralen]] bzw. [[Fließkommazahl|Fließkommatypen]]. |
Dank SFINAE spart man sich hier das separate Spezialisieren des Funktionstemplates auf alle einzelnen [[Integer (Datentyp)|integralen]] bzw. [[Fließkommazahl|Fließkommatypen]]. |
||
== Weblinks == |
== Weblinks == |
||
* [http://en.cppreference.com/w/cpp/language/sfinae SFINAE - cppreference.com] |
* [http://en.cppreference.com/w/cpp/language/sfinae SFINAE - cppreference.com] |
Version vom 30. Juni 2014, 11:41 Uhr
Substitution 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 schlussendlich verbliebenen Kandidaten ambiguos, beispielsweise weil sie dieselbe Signatur haben und sich nur durch den Rückgabewert unterscheiden, oder sind schlussendlich keine Kandidaten für den Funktionsaufruf mehr vorhanden, so wird dennoch wie üblich ein Kompilierfehler erzeugt.
Beispiele
Als Standardbeispiel sei ein Konstrukt genannt, das von einem Typen T
bestimmt, ob T::Type
ebenfalls ein Typ ist.
#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 Tester
-Funktion sind Referenzen, da in C++ bekanntlich keine Arrays zurückgegeben werden können. Der sizeof
-Operator gibt beim Anwenden auf eine Referenz die Größe des referenzierten Types zurück. Beim Anwenden auf einen vermeintlichen Funktionsaufruf (also auf eine Funktion inkl. Funktionsargumente) wird die Größe des Rückgabewertes angegeben. Da die Funktion in Tat und Wahrheit nie wirklich aufgerufen wird, ist keine Funktionsdefinition von Nöten. Um die Größe des Rückgabewertes angeben zu können, muss zuerst die korrekte Funktion ausgewählt werden. Wird als Template-Argument T = Foo
gewählt, so sind beide Überladungen korrekt. In diesem Fall wird anhand der Regeln überladener Funktionen die näherliegende ausgewählt. Da die variadische Funktion keine Typsicherheit bietet, wird die erste Funktion (die, die einen TrueType
zurückgibt) bevorzugt. Wird hingegen T = int
eingegeben, so wird die erste Funktion aus der Menge der infrage kommenden Überladungen ausgeschieden. Dies erzeugt dank SFINAE keinen Kompilierfehler. Es wird außerdem die Tatsache ausgenutzt, dass int
s in C++ implizit in Zeiger konvertiert werden.
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 std::enable_if
etc. hingegen sind auch in C++03 implementierbar.
Dank SFINAE spart man sich hier das separate Spezialisieren des Funktionstemplates auf alle einzelnen integralen bzw. Fließkommatypen.