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來說,已經是最好的選擇了。