I want to get into more template meta-programming. I know that SFINAE stands for "substitution failure is not an error." But can someone show me a good use for SFINAE?


Solution 1:

I like using SFINAE to check boolean conditions.

template<int I> void div(char(*)[I % 2 == 0] = 0) {
    /* this is taken when I is even */
}

template<int I> void div(char(*)[I % 2 == 1] = 0) {
    /* this is taken when I is odd */
}

It can be quite useful. For example, i used it to check whether an initializer list collected using operator comma is no longer than a fixed size

template<int N>
struct Vector {
    template<int M> 
    Vector(MyInitList<M> const& i, char(*)[M <= N] = 0) { /* ... */ }
}

The list is only accepted when M is smaller than N, which means that the initializer list has not too many elements.

The syntax char(*)[C] means: Pointer to an array with element type char and size C. If C is false (0 here), then we get the invalid type char(*)[0], pointer to a zero sized array: SFINAE makes it so that the template will be ignored then.

Expressed with boost::enable_if, that looks like this

template<int N>
struct Vector {
    template<int M> 
    Vector(MyInitList<M> const& i, 
           typename enable_if_c<(M <= N)>::type* = 0) { /* ... */ }
}

In practice, i often find the ability to check conditions a useful ability.

Solution 2:

Heres one example (from here):

template<typename T>
class IsClassT {
  private:
    typedef char One;
    typedef struct { char a[2]; } Two;
    template<typename C> static One test(int C::*);
    // Will be chosen if T is anything except a class.
    template<typename C> static Two test(...);
  public:
    enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
    enum { No = !Yes };
};

When IsClassT<int>::Yes is evaluated, 0 cannot be converted to int int::* because int is not a class, so it can't have a member pointer. If SFINAE didn't exist, then you would get a compiler error, something like '0 cannot be converted to member pointer for non-class type int'. Instead, it just uses the ... form which returns Two, and thus evaluates to false, int is not a class type.

Solution 3:

In C++11 SFINAE tests have become much prettier. Here are a few examples of common uses:

Pick a function overload depending on traits

template<typename T>
std::enable_if_t<std::is_integral<T>::value> f(T t){
    //integral version
}
template<typename T>
std::enable_if_t<std::is_floating_point<T>::value> f(T t){
    //floating point version
}

Using a so called type sink idiom you can do pretty arbitrary tests on a type like checking if it has a member and if that member is of a certain type

//this goes in some header so you can use it everywhere
template<typename T>
struct TypeSink{
    using Type = void;
};
template<typename T>
using TypeSinkT = typename TypeSink<T>::Type;

//use case
template<typename T, typename=void>
struct HasBarOfTypeInt : std::false_type{};
template<typename T>
struct HasBarOfTypeInt<T, TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>> :
    std::is_same<typename std::decay<decltype(std::declval<T&>().*(&T::bar))>::type,int>{};


struct S{
   int bar;
};
struct K{

};

template<typename T, typename = TypeSinkT<decltype(&T::bar)>>
void print(T){
    std::cout << "has bar" << std::endl;
}
void print(...){
    std::cout << "no bar" << std::endl;
}

int main(){
    print(S{});
    print(K{});
    std::cout << "bar is int: " << HasBarOfTypeInt<S>::value << std::endl;
}

Here is a live example: http://ideone.com/dHhyHE I also recently wrote a whole section on SFINAE and tag dispatch in my blog (shameless plug but relevant) http://metaporky.blogspot.de/2014/08/part-7-static-dispatch-function.html

Note as of C++14 there is a std::void_t which is essentially the same as my TypeSink here.

Solution 4:

Boost's enable_if library offers a nice clean interface for using SFINAE. One of my favorite usage examples is in the Boost.Iterator library. SFINAE is used to enable iterator type conversions.