Template Metaprogramming: SFINAE

In this blog let us discuss SFINAE (pronounced sphee-nay). Consider the following function

template <T>
void print(T val) {
std::cout << val;
}
The function can be called in various ways:
print(10.0)
will instatiate print<double>based on the above template. But you can also call
print<float>(10.0)
or
print(10.0f)
in which case print<float> will be instantiated. A third way would be to declare default parameters as in:
template <class T=int>
void print(T val) { ... }
We'll see a more practical example later. Given a call to template function or template class the compiler tries to match template based on some rules and then generates code for the best fit. If that code is ill-formed then there is no fit. But this is not necessarily an error. This is known as SFINAE - substitution failure is not an error. The compiler tries the next possible match recursively and as long as one match succeeds, the code is considered well formed. Consider
template <typename t="">
enable_if_t<is_integral<T>::value, T>
mysqrt(T  n)
{
  T c = 1 << (sizeof(T) * 8 / 2 - 1);
  T g = 0;
  while (c != 0)
  {
    if ((g + c)*(g + c) < n) g = g + c;
    c = c / 2;
  }
  return g;
}
template <typename T>
enable_if_t<is_floating_point<T>::value, double>
mysqrt(T  n)
{
  const double  epsilon = 2 * DBL_EPSILON;
  double s = n;
  double g = 1.0;
  while (abs(g*g - n) > epsilon)
  {
    g = (g + s / g) / 2;
  }

  return g;
}
Here is how SFINAE works. When the compiler sees mysqrt(2.0), it tries the first function but that is ill formed because enable_if_t does not generate a valid type. The compiler tries the next function definition and that succeeds. Now if you try mysqrt("Hello"), neither of the two functions will be well formed and the compiler will generate an error. Of course you can always create yet another function that caters to this case.

Proposed new type trait void_t

Walter Brown has prosed a new type trait:
template<class ...>
using void_t = void;
This does not compile in at least one compiler: Visual C++ 2013. Hence we use this definition
template <class ...>
struct voider { using type = void;};
template <class ...t0ton>
using void_t =typename voider<t0ton...>::type;
What is the use of yet another definition for void? Why do we need a symbol that represents nothing? Turns out it is very useful. Consider the following definitions:
//primary template:
template< class, class = void >
struct has_type_member : false_type { };
// partial specialization:
template< class T >
struct has_type_member< T, void_t< typename T::type > > :
  true_type { };
has_type_memeber::value is true if T::type is well formed (defined) and false otherwise. This magic is done by SFINAE. The general pattern is to first to declare a primary template that caters to the general case and then write partial template specialisations for the interesting cases. Another use for void_t is to check if a class is copyable.
template<class T>
using my_copy_assign_t = 
   decltype(std::declval(T&)=std::declval(const T&));

//	primary	template 
template<class t,class=void>
struct	is_copy_assignable_test: std::false_type{};
//
template<class t>
struct is_copy_assignable_test<
   t, void_t<my_copy_assign_t<t>>:
  std::true_type{};
If a class T is explicitly prohibited from being copied then is_copy_assignable_test::value would be false. Brown discusses many other interesting uses for void_t. Hope this brief blog has got you motivated.

Conclusion

To conclude, Template Meta Programming (TMP) is still rather esoteric. My aim was not to show how to write template meta programmes but to show how such code is written so that you can read template meta programs even if you can't write "real world" template meta programs.