Trusted answers to developer questions
Trusted Answers to Developer Questions

Related Tags

c++
concept

How to use the <requires> clause with concepts in C++ functions

Abdullah Arshad

A concept is a set of restrictions on template parameters evaluated at compile time. In this shot, we will write a concept in a function using the requires clause.

How to write the requires clause

We use the requires clause between the template parameter list and the function return type, which is auto in this case.

#include <iostream>
#include <concepts>
template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;

template <typename T>
requires Number<T>
auto add(T a, T b) {
  return a+b;
}

int main() {
  std::cout<<add(2,1.2f)<<'\n';
  return 0;
}
Add function requiring two same type of numbers

Note: We use the concept and define our constraint in the requires clause, expecting that any T template parameter must satisfy the concept Number requirements.

Using multiple types

If we want the capability of adding up numbers of multiple types, we’d need to introduce a second template parameter.

template <typename T,
          typename U>
requires Number<T> && Number<U>
auto add(T a, U b) {
  return a+b;
}

Then calls such as add(1, 2.14) will also work. Please note that the concept was modified. The drawback is that you’d need to introduce a new template parameter for each new function parameter and its requirement.

Multiple types of parameters in a template

It gives us some error because both parameters of the add function should be the same type.

If we want to add two numbers of multiple types, we have to change our template. Our template should look like this now:

#include <iostream>
#include <concepts>
template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;

template <typename T, typename U>
requires Number<T> && Number<U>
auto add(T a, U b) {
  return a+b;
}

int main() {
  std::cout<<add(5,42.1f)<<'\n';
  return 0;
}
Implementation of multiple types of parameters of a template

More constraints

We can also express more complex constraints with the requires clause. For the sake of an example, let’s just inline the definition of Number:

template <typename T>
requires std::integral<T> || std::floating_point<T>
auto add(T a, T b) {
  return a+b;
}

For better readability, in most cases, it’s a good idea to name your concepts, especially if you have more complex representations. Take a look at the code below for a full example.

#include <iostream>
#include <concepts>
template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;

template <typename T, typename U>
requires Number<T> && Number<U>
auto add(T a, U b) {
  return a+b;
}

template <typename T, typename U>
requires std::integral<T> || std::floating_point<T> &&\
         std::integral<U> || std::floating_point<U>

auto add2(T a, U b) {
  return a+b;
}

int main() {
  std::cout<<add(5,42.1f)<<'\n';
  std::cout<<add2(42.1f,5)<<'\n';
  return 0;
}
Naming concepts

The add() on line 8 is consuming a named concept, Number, using the requires clause. It takes two numbers as a parameter, which should be either integer or floating_point, and returns the sum of both numbers.

On line 16, another function, add2(), is defined, which takes two numbers as parameters and returns the sum, but uses an unnamed concept through the requires clause.

RELATED TAGS

c++
concept

CONTRIBUTOR

Abdullah Arshad
Copyright ©2022 Educative, Inc. All rights reserved
RELATED COURSES

View all Courses

Keep Exploring