【C++】SFINAE "Substitution Failure Is Not An Error"

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章