Create Safer Templates with Concepts and Constraints
Discover the way to create safer templates with concepts and constraints.
We'll cover the following...
template <typename T>T arg42(const T & arg) {return arg + 42;}
But what happens when we try to call it with a non-numeric type?
const char * n = "7";cout << "result is " << arg42(n) << "\n";
Output:
Result is ion
This compiles and runs without error, but the result is unpredictable. In fact, the call is dangerous and it could easily crash or become a vulnerability. It would be helpful if the compiler generated an error message, so we can fix the code.
Now, with concepts, it can be written like this:
template <typename T>requires Numeric<T>T arg42(const T & arg) {return arg + 42;}
The requires
keyword is new for C++20. It applies constraints to a template. Numeric
is the name of a concept that only accepts integer and floating-point types. Now, when this code is compiled with a non-numeric parameter, it gives a reasonable compiler error:
error: 'arg42': no matching overloaded function founderror: 'arg42': the associated constraints are not satisfied
Error messages like this are far more useful than most compiler errors.
Let's take a closer look at how to use concepts and constraints in our code.
How to do it
A concept is simply a named constraint. The Numeric
concept from above looks like this:
#include <concepts>template <typename T>concept Numeric = integral<T> || floating_point<T>;
This concept requires a type T
, which satisfies either the std::integral
or std::floating_point
...