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 language supportnew keywords, required concepts and predefined concepts from the standard library. In other words, this means that we can limit template parameters with the use of a natural and straightforward syntax. Before C ++ 20 came into use, these restrictions were added in various other ways.

There are three ways to use a concept with a class:

  • Using the requires clause
  • Trailing the requires clause
  • 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

Copyright ©2025 Educative, Inc. All rights reserved