Tag Archives: C-C++ basics and advanced

SFINAE of C++ idioms

What is SFINAE’s usage?
Before we get to SFINAE, let’s look at a piece of code, and it all starts with this piece of code.

template <typename T>
void show(typename T::iterator x, typename T::iterator y) {
    for (; x != y; ++x) cout << *x << endl;
}
int main() {
    show<int>(16, 18);
}

When you compile the above code, you will find that it fails to compile and you will get the following error.

sfinae.cc:13:21: error: no matching function for call to ‘show(int, int)’
     show<int>(16, 18);
                     ^
sfinae.cc:6:6: note: candidate: template<class T> void show(typename T::iterator, typename T::iterator)
 void show(typename T::iterator x, typename T::iterator y)
      ^
sfinae.cc:6:6: note:   template argument deduction/substitution failed:
sfinae.cc: In substitution of ‘template<class T> void show(typename T::iterator, typename T::iterator) [with T = int]’:
.................

To be clear, this error means that your template parameter type cannot be found; the template parameter is required to be a class type, and has an iterator class internal type. And then int doesn’t satisfy this requirement. No matching declaration was found so the following error was reported. It is easy to fix this error by adding an overload to the template as follows:

template <typename T>
void show(T a, T b) {
    cout << a << "; " << b << endl;
}

At this point the above code can run normally. This Is the so-called SFINAE solution, full name Is “Substitution Failure Is Not An Error,” this Is a feature of the compiler, for example when the compiler tries to according to the show (16, 18) , T to find: : iterator , this type of template matching found themselves unable to replace, it cause the compiler to sell the wrong then abruptly, but the compiler chose to ignore this Error and continue to find, Throw an error if you can't find anything by the end of all searches. This is called the SFINAE method. So the question is, what are the USES of SFINAE?", listen to the following analysis.
An example of SFINAE
SFINAE can be used to determine whether a type has an Iterator during compilation, as follows:

template <typename T>
struct has_iterator {
    template <typename U>
    static char test(typename U::iterator* x);
    template <typename U>
    static long test(U* x);
    static const bool value = sizeof(test<T>(0)) == 1;
};

In the above code, there are two test function templates, when the first one is matched, the char type is returned, so the size is 1, the value is true, when the second one is matched, the value is long, so the size is definitely not 1, and the value is false. This is a neat way to determine at compile time whether a type supports Iterator or not. Here is the test code:

int main() {
   has_iterator<vector<int> > test;
   if( test.value )
       cout << "vector have iterator" << endl;
   else
       cout << "vector not have iterator";
   has_iterator<int> test2;
   if( test2.value )
       cout << "int have iterator" << endl;
   else
       cout << "int not have iterator" << endl;

}

SFINAE in Boost/Standard library
SFINAE features are heavily used in boost and standard libraries, typically such as STD ::enable_if, STD ::is_class, STD ::void_t, etc. A large number of type traits are also heavily used in C++11. The following is the specific implementation of is_class :

template<typename T>
class is_class {
    typedef char yes[1];
    typedef char no [2];
    template<typename C> static yes& test(int C::*); // selected if C is a class type
    template<typename C> static no&  test(...);      // selected otherwise
  public:
    static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};

If it's a class, it matches the function that returns yes, and then value is true. This example is basically the same as the example above.