【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来说,已经是最好的选择了。

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