How to use the <requires> clause with concepts in C++ classes
What is a concept?
A concept is a set of restrictions on template parameters, which are evaluated at compile time. We can use a concept in class and function templates to manage function overloads and partial specializations. C++ 20 provides
There are three ways to use a concept with a class:
- Using the
requiresclause - Trailing the
requiresclause - Constrained template parameter
In this shot, we will write a concept in a class using the requires clause.
Note: If you want to learn all the three ways to use a concept with a class, then you should check out this course.
How to write the requires clause
We will use the same incomplete Number concept for the built-in numeric types that we used in this shot.
#include <concepts>template <typename T>concept Number = std::integral<T> ||std::floating_point<T>;
We can use the requires clause to define class template limits. We will create a class template, and after the template parameter list, we will add a requires clause on line 8, with restrictions that apply to our input.
#include <concepts>#include <iostream>template <typename T>concept Number = std::integral<T> || std::floating_point<T>;template <typename T>requires Number<T>class WrappedNumber {public:WrappedNumber(T num) : m_num(num) {}private:T m_num;};int main() {WrappedNumber wn{42};// WrappedNumber ws{"a string"}; // template constraint failure for 'template<class T> requires Number<T> class WrappedNumber'}
Code explanation
As we can see in the example, this is the same as the template class, except for the additional line of the requires clause.
If we use the template type name T in multiple places, the values we replace must be of the same type. If we use two restricted Ts in the constructor, they must also be of the same type. Even if both of them satisfy the concept of Number, they cannot be called by int and float.
A different declaration is required for the template parameter list and the potentially different usage limits for the template parameters.
#include <concepts>#include <iostream>template <typename T>concept Number = std::integral<T> || std::floating_point<T>;template <typename T, typename U>requires Number<T> && Number<U>class WrappedNumber {public:WrappedNumber(T num, U anotherNum) : m_num(num), m_anotherNum(anotherNum) {}private:T m_num;U m_anotherNum;};int main() {WrappedNumber wn{42, 5.4f};}
Line 9 of the code example given above shows that we can use compound expressions as constraints. That’s not possible with the other way to write constrained template classes.
Free Resources