I strongly believe concepts were the missing feature. Before them, a template didn't have a well-defined set of requirements, nor, in the case of a compilation error, a simple and brief description of it. These are the two pillars that drove the design of the concepts feature.
Step 1 includes the algorithms include for the std::sort method and the concepts header. To not confuse the compiler and ourselves, we encapsulated our new template in a namespace, sp. As you can see, there is a very minimal difference compared to the classical templates we used to use and the difference is with the requires keyword.
requires communicates to the compiler (and to the template user) that this template is only valid with a T Sortable type (Sortable<T>). OK; what is Sortable? This is a predicate that is only satisfied if it is evaluated to true. There are other ways to specify a constraint, as follows:
- With the trailing requires:
template<typename T>
void sort(T& container) requires Sortable<T>;
template<Sortable T>
void sort(T& container)
I personally prefer the style in the How to do it... section as it is more idiomatic and, more importantly, allows us to keep all the requires together, like so:
template<typename T>
requires Sortable<T> && Integral<T>
void sort(T& container)
{
std::sort (begin(container), end(container));
};
In this example, we want to communicate that our sp::sort method is valid with type T, which is Sortable and Integral, for whatever reason.
Step 2 simply uses our new customized version of sort. To do this, we instantiated a vector (which is Sortable!) and passed in input to the sp::sort method.