A concept (C++20) is a named, compile-time predicate on template arguments [temp.concept]. Constraints make templates state their requirements in the signature, and give shorter error messages than the SFINAE techniques they replace.
Defining a concept
concept-definition:
concept concept-name [attribute-specifier-seq] = constraint-expression ;
A concept is always a template, cannot itself be constrained, and cannot be specialised.
Its body is evaluated at compile time to true or false.
template<class T>
concept Numeric = std::integral<T> || std::floating_point<T>;
template<class T>
concept Sized = requires(T t) { { t.size() } -> std::convertible_to<std::size_t>; };
Applying constraints
There are several equivalent ways to constrain a template.
template<class T> requires Numeric<T>
T twice(T v); // requires clause after the head
template<class T>
T twice2(T v) requires Numeric<T>; // trailing requires clause
template<Numeric T>
T twice3(T v); // constrained template parameter
auto twice4(Numeric auto v); // abbreviated function template
A function parameter that uses auto makes the function an
abbreviated function template, with one invented type parameter for
each auto [dcl.fct]. A constrained placeholder Numeric auto
also works for variables, and is checked after deduction.
A requires clause is written requires expression, where the expression is a
primary expression, or several joined with && or ||
[temp.pre]. Anything more complex needs parentheses.
requires expressions
A requires expression (requires (params) { ... }) yields a
bool at compile time [expr.prim.req]. It contains requirements, which are
checked but never evaluated. The parameters are hypothetical variables.
| Requirement | Form | Checks | Section |
|---|---|---|---|
| Simple | expr; | That the expression is valid | [expr.prim.req.simple] |
| Type | typename T::name; | That the type exists | [expr.prim.req.type] |
| Compound | { expr } [noexcept] [-> constraint]; | That the expression is valid, optionally that it does not throw, and optionally that its type meets a concept | [expr.prim.req.compound] |
| Nested | requires constraint; | That a condition holds, using the parameters | [expr.prim.req.nested] |
template<class T>
concept Container = requires(T c) {
typename T::iterator; // type requirement
c.begin(); // simple requirement
{ c.size() } -> std::same_as<std::size_t>; // compound requirement
{ c.clear() } noexcept; // compound, must not throw
requires std::copyable<typename T::value_type>; // nested requirement
};
The type after -> must be a concept, and the expression’s
decltype((expr)) is its first argument. A requires expression with a substitution
failure is simply false, which is why it can be used as a test.
requires requires (T t) { ... } is valid: the first requires starts a
requires clause, and the second a requires expression.
Satisfaction and subsumption
Constraints are normalised into conjunctions and disjunctions of atomic constraints [temp.constr.normal]. Satisfaction is checked by substituting the arguments [temp.constr]. A substitution failure in an atomic constraint counts as not satisfied.
When two overloads both match, the more constrained one wins. Constraint A subsumes B if A implies B by looking at their atomic parts [temp.constr.order]. Two atomic constraints match only if they come from the same expression in the same concept, so writing the same condition twice does not create subsumption: name it as a concept.
template<class T> concept Base = std::integral<T>;
template<class T> concept Refined = Base<T> && (sizeof(T) >= 2);
template<Base T> void f(T) { } // 1
template<Refined T> void f(T) { } // 2
f(1); // calls 2: Refined subsumes Base
f('a'); // calls 1: Refined is not satisfied
Standard concepts
Most are in <concepts> [concepts]. The iterator row lists the main
ones; <iterator> also has more specialised algorithm requirements.
| Group | Concepts | Header |
|---|---|---|
| Core language | same_as derived_from convertible_to common_reference_with common_with integral signed_integral unsigned_integral floating_point assignable_from swappable swappable_with destructible constructible_from default_initializable move_constructible copy_constructible | <concepts> |
| Comparison | equality_comparable equality_comparable_with totally_ordered totally_ordered_with | <concepts> |
| Three-way | three_way_comparable three_way_comparable_with | <compare> |
| Object | movable copyable semiregular regular | <concepts> |
| Callable | invocable regular_invocable predicate relation equivalence_relation strict_weak_order | <concepts> |
| Iterators | input_or_output_iterator input_iterator output_iterator forward_iterator bidirectional_iterator random_access_iterator contiguous_iterator sentinel_for sized_sentinel_for indirectly_readable indirectly_writable weakly_incrementable incrementable | <iterator> |
| Ranges | ranges::range ranges::borrowed_range ranges::sized_range ranges::view ranges::input_range ranges::output_range ranges::forward_range ranges::bidirectional_range ranges::random_access_range ranges::contiguous_range ranges::common_range ranges::viewable_range ranges::constant_range (C++23) | <ranges> |