Template Metaprogramming: SFINAE

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

template 
void print(T val) {
 std::cout << val;
 }

The function can be called in various ways:

print(10.0)

will instantiate print based on the above template. But you can also call

print(10.0f)

in which case print will be instantiated.

A third way would be to explicitly state the template arguments as in:

print(10.0);

Given a call to template function or template class instantiation the compiler tries to match template based on the above rules and then generates code for the best fit. If that code is ill-formed then it 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 
enable_if_t
 mysqrt(T  n)
{
	T c = 1 << (sizeof(T) * 8 / 2 - 1);
	T g = 0;
	while (c != 0)
	{
		if ((g + c)*(g + c)  epsilon)
	{
		g = (g + s / g) / 2;
	}

	return g;
}
mysqrt(2.0);

Here is how SFINAE works. When the compiler sees mysqrt(2.0), it decides that the template parameter is double because ‘2.0’ is double. It substitutes T with double in the first function but that is ill formed because enable_if_t does not generate a valid type. This substitution failure is not an error. 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 proposed a new type trait:

template using void_t = void;

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 
  struct has_type_member : false_type { }; 
  
// partial specialization: 
template
struct has_type_member< T, 
    void_t> : true_type 
{ }; 
 

has_type_member::value is true if T::type is well formed (defined) and false otherwise. This magic is done by SFINAE. If a class A does have definition for type either through a typedef or an internal class definition, then has_type_member::value will coalesce to creating

has_type_member <A,void_t
>;:true_type

as it is the more specialised version. Otherwise it will default to

has_type_member ::false_type

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, Brown suggests for void_t is to check if a class is copyable.


template
  using	copy_assign_t  =  decltype(
         declval()  = declval());	

Primary template handles all non-­‐copy-­‐assignable types:

  
template
struct	is_copy_assignable:false_type {	};	
  
//  specialization	
template
struct is_copy_assignable<T, 
      void_t< copy_assign_t>	
  :  true_type	{  }; 	
  

If a class T is explicitly prohibited from being copied then is_copy_assignable::value would be false.

To conclude, Template Meta Programming (TMP) is still rather esoteric. As I mentioned in my previous blog, the aim was to show how such template meta programs are written so that you can read even if you can’t write “real world” template meta programs.

About The Sunday Programmer

Joe is an experienced C++/C# developer on Windows. Currently looking out for an opening in C/C++ on Windows or Linux.
This entry was posted in C++, Software Engineering and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s