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

template<typename T>
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<float>(10.0f)

in which case print 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 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 <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 - s) > 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 prosed a new type trait:

template<class ...>
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 <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_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<A>::value will coalesce to creating

has_type_member <A,void_t <A::type>>:true_type

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

has_type_member <A,void>::false_type

For a more detailed explanation see dyp’s answer.

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<class	T>
using my_copy_assign_t =
     decltype(std::declval<T&>()=std::declval<T const&>());
//	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.

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.

Advertisements

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s