This rule applies during overload resolution of function templates: When substituting the explicitly specified or deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error.
This feature is used in template metaprogramming.
for example:
template <typename A>
struct B { using type = typename A::type; };
template <
class T,
class U = typename T::type, // SFINAE failure if T has no member type
class V = typename B<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14 because
// substitution into the default template argument
// of U would fail first)
> void foo (int);
template <typename T> void inc_counter(
T& counterObj,
typename std::enable_if<
is_base_of<T, ICounter>::value
>::type* = nullptr );
template <typename T> void inc_counter(
T& counterInt,
typename std::enable_if<
std::is_integral<T>::value
>::type* = nullptr );
首先,substitution只有在推断函数类型的时候,才会起作用。推断函数类型需要参数的类型,所以,typename std::enable_if<std::is_integral::value>::type 这么一长串代码,就是为了让enable_if参与到函数类型中;
其次,is_integral::value返回一个布尔类型的编译器常数,告诉我们它是或者不是一个integral,enable_if的作用就是,如果这个C值为True,那么type就会被推断成一个void或者是别的什么类型,让整个函数匹配后的类型变成 void inc_counter(int & counterInt, void* dummy = nullptr); 如果这个值为False,那么enable_if这个特化形式中,压根就没有这个::type,于是substitution就失败了 —— 所以这个函数原型根本就不会被产生出来。
所以我们能保证,无论对于int还是counter类型的实例,我们都只有一个函数原型是通过了substitution —— 这样就保证了它的“有且唯一”,编译器也不会因为你某个替换失败而无视成功的那个实例。
SFINAE最主要的作用,是保证编译器在泛型函数、偏特化、及一般重载函数中遴选函数原型的候选列表时不被打断。除此之外,它还有一个很重要的元编程作用就是实现部分的编译期自省和反射。
虽然它写起来并不直观,但是对于既没有编译器自省、也没有Concept的C++1y来说,已经是最好的选择了。