Create Safer Templates with Concepts and Constraints
Explore C++20 concepts and constraints to make templates safer and more reliable. Understand how to use the requires keyword, predefined concepts, and custom constraints to prevent errors and improve code clarity in generic programming.
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 predefined concepts. These concepts are included ...