Template Constraints

Templates in C++, by default, are unconstrained. So it gives us a simple form of static duck-typing. As a simple example, lets look at an increment and twice function:

template<class T> void increment(T& x) { ++x; } template<class T> void twice(T& x) { increment(x); increment(x); }

Well, if we call the twice function with an integer, it will be a number incremented twice:

int i = 1; twice(i); std::cout << i; // Prints 3

Now since the template parameters are unconstrained if we pass a type that in not an integer.

foo f; twice(f);

We will get a compile error like this:

twice.cpp:6:5: error: cannot increment value of type 'foo' ++x; ^ ~ twice.cpp:12:5: note: in instantiation of function template specialization 'increment<foo>' requested here increment(x); ^ twice.cpp:25:5: note: in instantiation of function template specialization 'twice<foo>' requested here twice(f); ^

However, this error will occur in the increment function(with a backtrace to our twice function call) and not at the call to twice . Of course in this simple example its easy to see whats wrong, but if twice was library code its unclear if the error is due to a mistake in the library or whether its a mistake made by the user. So we should specify type requirements for each of our template parameters, and then add constraints to these template parameters.

For now, we will require the types to be integers, so we can use std::enable_if to constraint the template parameter(which uses something called subtitution failure to constrain the template). There are several different forms to use std::enable_if depending on the context. The most common form is to use a default template parameter, like this:

template<class T, typename std::enable_if<(std::is_integral<T>()), int>::type = 0> void increment(T& x) { ++x; }

Now, since there is lot of noise to enable_if , it is better to make a little macro to improve readability:

#define REQUIRES(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0 template<class T, REQUIRES(std::is_integral<T>())> void increment(T& x) { ++x; } template<class T, REQUIRES(std::is_integral<T>())> void twice(T& x) { increment(x); increment(x); }

So this uses the std::is_integral type trait to check that the type is an integer. So now if we try to compile the previous code:

foo f; twice(f);

We will get a compile error like this:

twice.cpp:23:5: error: no matching function for call to 'twice' twice(f); ^~~~~ twice.cpp:11:19: note: candidate template ignored: disabled by 'enable_if' [with T = foo] template<class T, REQUIRES(std::is_integral<T>())> ^

Which first points the error in the user code, and then shows the type requirements for the function.

Custom Type Trait

So we used std::is_integral type trait, however this is too restrictive. The increment could work on floating points and pointers. We could create a custom type trait using std::integral_constant like this:

template<class T> struct is_incrementable : std::integral_constant<bool, ( std::is_integral<T>() && std::is_floating_point<T>() && std::is_pointer<T>())> {};

However, still that doesn’t cover everything. Instead we can create a trait and constrain it by a valid expression. First we create the trait as false with a default template parameter:

template<class T, class Enable=void> struct is_incrementable : std::false_type {};

Now the Enable parameter is what we will use to check for a valid expression. So we specialize is_incrementable , but we pass in always_void for the specialization of the Enable parameter(its void because that is what we set the default parameter to). The always_void template will allow us to put expressions that we want to check for:

template<class T> struct always_void { typedef void type; }; template<class T> struct is_incrementable<T, typename always_void< decltype(++std::declval<T&>()) >::type> : std::true_type {};

So if decltype(++std::declval<T&>()) is not a valid exression the compiler will pick the default definition. However, there is a lot of boilerplate involved in setting this up, plus its a little ugly to read with all the std::declval . Instead, we can take advantage of the fact the we can constrain a function using a trailing decltype (from Eric Niebler’s blog post here), like this:

struct Incrementable { template<class T> auto requires_(T&& x) -> decltype(++x); };

So if ++x is not valid, then the requires_ member function is not callable. So we can create a models trait that just checks if requires_ is callable:

template<class Concept, class Enable=void> struct models : std::false_type {}; template<class Concept, class... Ts> struct models<Concept(Ts...), typename always_void< decltype(std::declval<Concept>().requires_(std::declval<Ts>()...)) >::type> : std::true_type {};

This uses the function signature syntax for a template so it will work with any number of paramenters to the requires_ function. So using the models trait we have simple way to define and check for type requirements.

So now we can define our is_incrementable trait like this:

template<class T> struct is_incrementable : models<Incrementable(T)> {};

Or we could just use the models trait in our requires clause like this: