Concepts and Template Constraints
Templates and Generic Programming

5.4 Concepts and Template Constraints

Historically, C++ templates were completely unconstrained. If you wrote a function that added two variables using the + operator and a developer passed a type that did not support addition, the compiler would attempt to build the function body anyway, resulting in massive compiler errors that were incredibly difficult to parse.

C++20 solved this problem by introducing Concepts, allowing you to explicitly constrain template parameters.

The Nightmare of Unconstrained Errors

If a template compilation fails deep inside a nested function, the compiler prints every single attempted template substitution path. This creates a wall of error text. Concepts fix this by validating types before compiling the function body.

C++20 Concepts and the `requires` Clause

Concepts define constraints that types must satisfy. The standard library provides built in concepts like std::integral, std::floating_point, and std::copyable:

#include <concepts>
#include <iostream>

// This function only accepts types that satisfy the std::integral concept!
template <typename T>
requires std::integral<T>
T addIntegers(T a, T b) {
    return a + b;
}

// Cleaner shorthand syntax using constrained auto parameters
void printIntegral(std::integral auto x) {
    std::cout << "Integer: " << x << '\n';
}
Constraining templates with integral concepts.

Creating Custom Concepts

You can define your own concepts using the concept keyword and requires expressions to check for method signatures or operators:

#include <concepts>

// Define a custom concept that checks if a type can be incremented
template <typename T>
concept Incrementable = requires(T x) {
    { ++x } -> std::same_as<T&>;
};

template <typename T>
requires Incrementable<T>
void incrementValue(T& val) {
    ++val;
}
Creating a custom concept constraint.

* The Security Guard Metaphor: Think of C++20 Concepts like hiring a security guard at the door of your function. In old C++ templates, anyone could enter. The compiler only noticed they did not fit when they started breaking chairs inside (attempting to use operators they did not support), forcing a chaotic cleanup. Concepts stop visitors at the door, check their type credentials, and reject them instantly if they do not match, producing a clean error message.

Finished reading this lesson?