๐ŸŽ“ A1Learner
๐Ÿ”
0 of 28 lessons completed

C++ Requires Clauses

A requires clause is a condition written after a template head. The template only accepts types that meet the condition.

A clause after the parameter list

The clause follows the parameter list of addScores. It checks the type against the Addable concept before the body runs.

#include <iostream>
#include <concepts>

template <typename T>
concept Addable = requires(T a, T b) {
  a + b;
  a == b;
};

template <typename T>
T addScores(T a, T b) requires Addable<T> {
  return a + b;
}

int main() {
  std::cout << addScores(40, 2) << std::endl;
  std::cout << addScores(10, 5) << std::endl;
  return 0;
}

A clause on a class template

The Store class template carries the same kind of clause. An int meets the concept, so the box can hold it.

#include <iostream>
#include <concepts>

template <typename T>
concept Addable = requires(T a, T b) {
  a + b;
  a == b;
};

template <typename T>
requires Addable<T>
class Store {
public:
  T value;
  Store(T v) {
    value = v;
  }
};

int main() {
  Store<int> box(42);
  std::cout << box.value << std::endl;
  return 0;
}

A clause picks between overloads

Two show templates share one name. The int call meets Addable and picks the constrained one, while the Tag call falls back to the plain one.

#include <iostream>
#include <concepts>

template <typename T>
concept Addable = requires(T a, T b) {
  a + b;
  a == b;
};

struct Tag {};

template <typename T>
void show(T value) {
  std::cout << "General value" << std::endl;
}

template <typename T>
void show(T value) requires Addable<T> {
  std::cout << "Addable value: " << value + value << std::endl;
}

int main() {
  show(21);
  show(Tag{});
  return 0;
}

A failing type is refused

Tag has no addition, so it fails the Addable concept. The compiler refuses the call and names it in the first error line.

#include <iostream>
#include <concepts>

template <typename T>
concept Addable = requires(T a, T b) {
  a + b;
  a == b;
};

template <typename T>
T addScores(T a, T b) requires Addable<T> {
  return a + b;
}

struct Tag {};

int main() {
  Tag first;
  Tag second;
  std::cout << addScores(first, second) << std::endl;
  return 0;
}

Example

#include <iostream>
#include <concepts>

template <typename T>
concept Addable = requires(T a, T b) {
  a + b;
  a == b;
};

template <typename T>
T addPair(T a, T b) requires Addable<T> {
  return a + b;
}

template <typename T>
T triple(T a) requires Addable<T> {
  return a + a + a;
}

int main() {
  std::cout << addPair(2, 3) << std::endl;
  std::cout << triple(1.5) << std::endl;
  return 0;
}

Key points

  • A requires clause starts with the word requires.
  • The clause sits after the parameter list of a function template.
  • The clause holds a concept check such as Addable<T>.
  • A class template can carry a requires clause too.
  • When two overloads match a call, the compiler picks the one whose concept the type meets.
  • A call with a type that fails the concept does not compile.

In this example, both functions carry a requires clause with the Addable concept. The addPair call adds 2 and 3 and prints 5. The triple call adds 1.5 three times and prints 4.5.

Note

Each program defines its own concepts in full, so every block stands alone.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

Where does a requires clause sit on a function template?