Substitution failure is not an error

from Wikipedia, the free encyclopedia

Substitution failure is not an error ( SFINAE ) is a programming technique in the C ++ programming language . This takes advantage of the fact that a failed substitution of template arguments does not cause a compilation error.

If, for example, a function is overloaded several times and certain candidates are the result of the instantiation of a function template with possibly deduced template arguments, then - if the substitution of the template arguments has failed - the overload is removed from the set of candidates without the compilation process using aborts a compile error. If the remaining candidates are ambiguous, for example because they have the same signature and only differ in the return value, or if there are no more candidates for the function call, a compile error is still generated as usual.

Examples

A standard example is a construct that Tdetermines whether T::Typea type is also a type.

#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
}

The return values ​​of the Testerfunction are references , since, as is well known, arrays cannot be returned in C ++. The sizeofoperator returns the size of the referenced type when applied to a reference. When applying to a supposed function call (i.e. to a function including function arguments), the size of the return value is specified. Since the function is never actually called, no function definition is required. In order to be able to specify the size of the return value, the correct function must first be selected. If the template argument is T = Foochosen, both overloads are correct. In this case, using the rules of overloaded functions, the closer one is selected. Since the variadic function offers no type safety , the first function (the one TrueTypethat returns one) is preferred. If, on the other hand, is T = intentered, the first function is eliminated from the set of overloads in question. Thanks to SFINAE, this does not generate any compilation errors. It also takes advantage of the fact that the 0C ++ value can be implicitly converted to a null pointer .

A popular place where SFINAE is used is the restriction or specialization in types that share a certain commonality. An example:

#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
}

Because of the default template arguments, the program cannot run in C ++ 03, but only in C ++ 11. The elements std::enable_ifetc., however, can also be implemented in C ++ 03.

Thanks to SFINAE, there is no need to separately specialize the function template on all individual integral or floating point types .

Web links