SFINAE (Substitution failure is not an error) refers to a situation where template substitution is artifically forced to fail if a testable condition is not met. This can be useful in conjunction with type traits. In the present code, a custom type trait is used to detect if a template parameter type has a member function named foo
. The general definition of has_foo
extends the false_type
base class whereas a partial template specialization extends true_type
. The specialization takes place in the second, unnamed, template parameter which otherwise defaults to void
. In the specialization, this is replaced by the void_t
type defined beforehand. This also just aliases void
but accepts unnamed (variadic) template parameters. Now, in the template specialization of has_foo
, a function pointer type to T::foo
is passed as a template parameter to void_t
. Despite void_t
not doing anything with it, this will cause the template substitution to fail if and only if T
does not have a member function foo
. This failure, however, is not considered an error! It merely causes the compiler not to apply the template specialization (extending true_type
) for types not sporting a foo
member function but to fall back to the generic template (extending false_type
). This trait may be used to impose and test for required properties of template parameter types using static_assert
.
Last active
May 16, 2016 09:53
-
-
Save jgreitemann/17fd2de9994db3929e7a3442e9decf12 to your computer and use it in GitHub Desktop.
SFINAE
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <type_traits> | |
using namespace std; | |
template <typename...> using void_t = void; | |
template <typename T, typename = void> | |
struct has_foo : false_type {}; | |
template <typename T> | |
struct has_foo<T, void_t<decltype(&T::foo)>> : true_type {}; | |
struct bar { | |
void foo() { | |
cout << "foo" << endl; | |
} | |
}; | |
struct baz { | |
void fool() { | |
cout << "fool" << endl; | |
} | |
}; | |
template <typename T> | |
void call_foo(T t) { | |
static_assert(has_foo<T>::value, | |
"T does not have member function foo"); | |
t.foo(); | |
} | |
int main() { | |
cout << boolalpha | |
<< has_foo<bar>::value << endl | |
<< has_foo<baz>::value << endl; | |
call_foo(bar()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment