template <std::integral T>
T doubleIt(T x) { return x + x; } // T must be an integer type
C++
Topics in this category
Casting & Conversions
Compilation & Linking
Concepts & Constraints
Concurrency
Const Correctness
Constexpr & Compile-Time
Coroutines
Enums & Enum Classes
Exception Handling
I/O Streams
Lambda Expressions
Memory Management
Modern C++
Move Semantics
Namespaces
OOP & Classes
Operator Overloading
Preprocessor & Macros
RAII & Smart Pointers
Ranges & Views
C++
Concepts & Constraints
Concepts are named, compile-time predicates that constrain template parameters—specifying the requirements a type must satisfy (e.g., must be sortable, must support +). They make templates clearer, produce far better error messages than raw templates or SFINAE, and let the compiler reject invalid types early with a precise reason.
Real-world example
Constraining a numeric template with std::integral gives a clear error when a string is passed.
Templates
Concepts & Constraints
Concepts & Constraints
Without concepts, using a template with an unsupported type produces long, cryptic errors deep inside the template's body. Concepts check requirements at the call site and report a concise message like 'constraint not satisfied' naming the failed requirement, so mistakes are caught early and explained clearly.
template <class T> requires std::equality_comparable<T>
bool same(T a, T b) { return a == b; }
Real-world example
Passing a non-comparable type now yields 'does not satisfy equality_comparable' instead of a wall of template errors.
Templates
Concepts & Constraints
Concepts & Constraints
You can constrain templates via: a concept name in place of typename (template <std::integral T>), a requires clause (template <class T> requires C<T>), a trailing requires clause after the function signature, or the abbreviated function template syntax using a concept with auto (void f(std::integral auto x)). All express the same kind of requirement.
void f(std::integral auto x); // abbreviated
template <class T> requires C<T> void g(T); // requires clause
Real-world example
The team uses the abbreviated 'std::sortable auto' parameter form for concise constrained functions.
Templates
Type Deduction & auto
Concepts & Constraints
A requires expression (requires(T a){ ... }) is a compile-time check that yields true if the enclosed operations/requirements are valid for the given types. It's used to define concepts, listing required expressions, their validity, and return-type constraints. It replaces verbose SFINAE with readable requirement lists.
template <class T>
concept Addable = requires(T a, T b) { { a + b } -> std::same_as<T>; };
Real-world example
A custom Addable concept is defined with a requires expression listing that a + b must be valid and return T.
Templates
Concepts & Constraints
Concepts & Constraints
SFINAE with std::enable_if constrains templates by making invalid substitutions silently remove overloads—powerful but cryptic and hard to read/maintain. Concepts express the same constraints declaratively, give clear diagnostics, participate cleanly in overload resolution (more-constrained overloads are preferred), and are far more readable. Concepts are the modern replacement for most SFINAE.
// old: template <class T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>
// new: template <std::integral T>
Real-world example
Rewriting enable_if-heavy code with concepts makes constraints readable and error messages clear.
Templates
Concepts & Constraints
Concepts & Constraints
The standard library defines many concepts in <concepts> and elsewhere: std::integral, std::floating_point, std::same_as, std::convertible_to, std::equality_comparable, std::totally_ordered, std::copyable, std::movable, std::invocable, and range/iterator concepts. They cover common type requirements so you rarely need to write your own.
template <std::floating_point T> T half(T x){ return x/2; }
Real-world example
Constraining math helpers with std::floating_point documents and enforces that only real-number types are accepted.
Templates
Concepts & Constraints
Concepts & Constraints
Define a concept with the concept keyword equal to a compile-time boolean expression, often a requires expression listing required operations and their constraints. The concept can then constrain templates. Compose concepts with && and || and refine existing ones to build precise, reusable requirements.
template <class T>
concept Container = requires(T c) {
c.begin(); c.end(); typename T::value_type;
};
Real-world example
A project defines a Container concept so generic algorithms clearly require begin/end and value_type.
Templates
Concepts & Constraints
Concepts & Constraints
When multiple constrained overloads match, the compiler prefers the one whose constraints subsume (are more specific/stronger than) the others. This lets you write a general constrained template plus a more-constrained specialization, and the most-constrained viable candidate is chosen—cleanly replacing tag dispatch and enable_if priority tricks.
void f(std::integral auto x); // more constrained
void f(auto x); // less constrained
f(5); // picks the std::integral overload
Real-world example
A more-constrained overload for random-access ranges is automatically preferred over the generic one.
Templates
Concepts & Constraints
Concepts & Constraints
Unconstrained templates accept any type and only fail deep inside instantiation with confusing errors, and they can silently participate in overload resolution for wrong types. Concepts document intent, reject invalid types at the interface with clear messages, improve tooling/IDE support, and make generic code self-explanatory and safer.
Real-world example
Adding concepts to a library's templates turns baffling instantiation errors into clear, early diagnostics.
Templates
Concepts & Constraints
Concepts & Constraints
The ranges library is built on concepts: it defines iterator and range concepts (input_iterator, sized_range, etc.) that constrain every algorithm and view, so misuse is caught at compile time with clear messages, and adaptors compose only with compatible ranges. Concepts are foundational to ranges' safety and expressiveness.
Real-world example
A view adaptor that needs a forward range fails to compile clearly when given a single-pass input range.
Ranges & Views
Templates
Concepts & Constraints