Skip to main content
Template constraints are a C++20 language feature that enforces compile-time requirements on template arguments. They restrict the set of types or values that can be substituted into a template parameter list, causing substitution failure if the requirements are not met. This mechanism replaces complex SFINAE (Substitution Failure Is Not An Error) metaprogramming with direct boolean constant expressions, enabling the compiler to perform strict type-checking before template instantiation.

Core Components

The constraint system is built upon three primary language constructs: the requires clause, the concept definition, and the requires expression.

1. The requires Clause

A requires clause appends a boolean constant expression to a template declaration. If the expression evaluates to false, the template is removed from the overload resolution set.

2. Concepts

A concept is a named set of constraints. It is defined at namespace scope and evaluates to a boolean prvalue. Concepts cannot be recursively defined and cannot be explicitly instantiated.

3. Application Syntax

Constraints can be applied to templates in four distinct syntactic forms, all of which are semantically equivalent:

The requires Expression

A requires expression is a distinct language feature from the requires clause. It is an operator that yields a boolean prvalue based on whether a set of hypothetical expressions and types are well-formed. It does not evaluate the expressions at runtime. A requires expression can contain four types of requirements:
  1. Simple Requirements: Asserts that an expression is valid (compiles).
  2. Type Requirements: Asserts that a nested type or class template specialization exists.
  3. Compound Requirements: Asserts expression validity, checks noexcept status, and constrains the return type.
  4. Nested Requirements: Evaluates additional boolean constant expressions.

Constraint Normalization and Subsumption

When multiple constrained templates are viable during overload resolution, the compiler determines the best match through a process called subsumption. To perform subsumption, the compiler normalizes constraints by expanding concept definitions into a structural tree of conjunctions (logical ANDs) and disjunctions (logical ORs) of atomic constraints. A constraint PP subsumes a constraint QQ if PP implies QQ. The compiler will select the most constrained viable template.
Subsumption only works with named concept definitions. Inline requires clauses using identical type traits do not subsume one another because the compiler considers atomic constraints identical only if they originate from the same appearance of the same expression in the source code (i.e., the exact same lexical location). They are not evaluated by structural equivalence.
Tired of Poor C++ Skills? Fix That With Deep Grasping!Learn More