Class templates, function templates, and nontemplate functions (typically members of class templates) may be associated with a constraint, which specifies the requirements on template arguments, which can be used to select the most appropriate function overloads and template specializations.
Named sets of such requirements are called concepts. Each concept is a predicate, evaluated at compile time, and becomes a part of the interface of a template where it is used as a constraint:
#include <string> #include <cstddef> #include <concepts> // Declaration of the concept "Hashable", which is satisfied by any type 'T' // such that for values 'a' of type 'T', the expression std::hash<T>{}(a) // compiles and its result is convertible to std::size_t template<typename T> concept Hashable = requires(T a) { { std::hash<T>{}(a) } > std::convertible_to<std::size_t>; }; struct meow {}; // Constrained C++20 function template: template<Hashable T> void f(T) {} // // Alternative ways to apply the same constraint: // template<typename T> // requires Hashable<T> // void f(T) {} // // template<typename T> // void f(T) requires Hashable<T> {} // // void f(Hashable auto /*parameterName*/) {} int main() { using std::operator""s; f("abc"s); // OK, std::string satisfies Hashable // f(meow{}); // Error: meow does not satisfy Hashable }
Violations of constraints are detected at compile time, early in the template instantiation process, which leads to easy to follow error messages:
std::list<int> l = {3, 1, 10}; std::sort(l.begin(), l.end()); // Typical compiler diagnostic without concepts: // invalid operands to binary expression ('std::_List_iterator<int>' and // 'std::_List_iterator<int>') // std::__lg(__last  __first) * 2); // ~~~~~~ ^ ~~~~~~~ // ... 50 lines of output ... // // Typical compiler diagnostic with concepts: // error: cannot call std::sort with std::_List_iterator<int> // note: concept RandomAccessIterator<std::_List_iterator<int>> was not satisfied
The intent of concepts is to model semantic categories (Number, Range, RegularFunction) rather than syntactic restrictions (HasPlus, Array). According to ISO C++ core guideline T.20, "The ability to specify meaningful semantics is a defining characteristic of a true concept, as opposed to a syntactic constraint."
A concept is a named set of requirements. The definition of a concept must appear at namespace scope.
The definition of a concept has the form.
template < templateparameterlist >

attr    sequence of any number of attributes 
// concept template<class T, class U> concept Derived = std::is_base_of<U, T>::value;
Concepts cannot recursively refer to themselves and cannot be constrained:
template<typename T> concept V = V<T*>; // error: recursive concept template<class T> concept C1 = true; template<C1 T> concept Error1 = true; // Error: C1 T attempts to constrain a concept definition template<class T> requires C1<T> concept Error2 = true; // Error: the requiresclause attempts to constrain a concept
Explicit instantiations, explicit specializations, or partial specializations of concepts are not allowed (the meaning of the original definition of a constraint cannot be changed).
Concepts can be named in an idexpression. The value of the idexpression is true
if the constraint expression is satisfied, and false
otherwise.
Concepts can also be named in a typeconstraint, as part of.
In a typeconstraint, a concept takes one less template argument than its parameter list demands, because the contextually deduced type is implicitly used as the first argument of the concept.
template<class T, class U> concept Derived = std::is_base_of<U, T>::value; template<Derived<Base> T> void f(T); // T is constrained by Derived<T, Base>
A constraint is a sequence of logical operations and operands that specifies requirements on template arguments. They can appear within requires expressions or directly as bodies of concepts.
There are three types of constraints:
The constraint associated with a declaration are determined by normalizing a logical AND expression whose operands are in the following order:
This order determines the order in which constraints are instantiated when checking for satisfaction.
A constrained declaration may only be redeclared using the same syntactic form. No diagnostic is required:
// These first two declarations of f are fine template<Incrementable T> void f(T) requires Decrementable<T>; template<Incrementable T> void f(T) requires Decrementable<T>; // OK, redeclaration // Inclusion of this third, logicallyequivalentbutsyntacticallydifferent // declaration of f is illformed, no diagnostic required template<typename T> requires Incrementable<T> && Decrementable<T> void f(T); // The following two declarations have different constraints: // the first declaration has Incrementable<T> && Decrementable<T> // the second declaration has Decrementable<T> && Incrementable<T> // Even though they are logically equivalent. template<Incrementable T> void g(T) requires Decrementable<T>; template<Decrementable T> void g(T) requires Incrementable<T>; // illformed, no diagnostic required
The conjunction of two constraints is formed by using the &&
operator in the constraint expression:
template<class T> concept Integral = std::is_integral<T>::value; template<class T> concept SignedIntegral = Integral<T> && std::is_signed<T>::value; template<class T> concept UnsignedIntegral = Integral<T> && !SignedIntegral<T>;
A conjunction of two constraints is satisfied only if both constraints are satisfied. Conjunctions are evaluated left to right and shortcircuited (if the left constraint is not satisfied, template argument substitution into the right constraint is not attempted: this prevents failures due to substitution outside of immediate context).
template<typename T> constexpr bool get_value() { return T::value; } template<typename T> requires (sizeof(T) > 1 && get_value<T>()) void f(T); // #1 void f(int); // #2 void g() { f('A'); // OK, calls #2. When checking the constraints of #1, // 'sizeof(char) > 1' is not satisfied, so get_value<T>() is not checked }
The disjunction of two constraints is formed by using the 
operator in the constraint expression.
A disjunction of two constraints is satisfied if either constraint is satisfied. Disjunctions are evaluated left to right and shortcircuited (if the left constraint is satisfied, template argument substitution into the right constraint is not attempted).
template<class T = void> requires EqualityComparable<T>  Same<T, void> struct equal_to;
An atomic constraint consists of an expression E
and a mapping from the template parameters that appear within E
to template arguments involving the template parameters of the constrained entity, called its parameter mapping.
Atomic constraints are formed during constraint normalization. E
is never a logical AND or logical OR expression (those form conjunctions and disjunctions, respectively).
Satisfaction of an atomic constraint is checked by substituting the parameter mapping and template arguments into the expression E
. If the substitution results in an invalid type or expression, the constraint is not satisfied. Otherwise, E
, after any lvaluetorvalue conversion, shall be a prvalue constant expression of type bool
, and the constraint is satisfied if and only if it evaluates to true
.
The type of E
after substitution must be exactly bool
. No conversion is permitted:
template<typename T> struct S { constexpr operator bool() const { return true; } }; template<typename T> requires (S<T>{}) void f(T); // #1 void f(int); // #2 void g() { f(0); // error: S<int>{} does not have type bool when checking #1, // even though #2 is a better match }
Two atomic constraints are considered identical if they are formed from the same expression at the source level and their parameter mappings are equivalent.
template<class T> constexpr bool is_meowable = true; template<class T> constexpr bool is_cat = true; template<class T> concept Meowable = is_meowable<T>; template<class T> concept BadMeowableCat = is_meowable<T> && is_cat<T>; template<class T> concept GoodMeowableCat = Meowable<T> && is_cat<T>; template<Meowable T> void f1(T); // #1 template<BadMeowableCat T> void f1(T); // #2 template<Meowable T> void f2(T); // #3 template<GoodMeowableCat T> void f2(T); // #4 void g() { f1(0); // error, ambiguous: // the is_meowable<T> in Meowable and BadMeowableCat forms distinct atomic // constraints that are not identical (and so do not subsume each other) f2(0); // OK, calls #4, more constrained than #3 // GoodMeowableCat got its is_meowable<T> from Meowable }
Constraint normalization is the process that transforms a constraint expression into a sequence of conjunctions and disjunctions of atomic constraints. The normal form of an expression is defined as follows:
(E)
is the normal form of E
; E1 && E2
is the conjunction of the normal forms of E1
and E2
. E1  E2
is the disjunction of the normal forms of E1
and E2
. C<A1, A2, ... , AN>
, where C
names a concept, is the normal form of the constraint expression of C
, after substituting A1, A2, ... , AN for C
's respective template parameters in the parameter mappings of each atomic constraint of C. If any such substitution into the parameter mappings results in an invalid type or expression, the program is illformed, no diagnostic required. template<typename T> concept A = T::value  true; template<typename U> concept B = A<U*>; // OK: normalized to the disjunction of //  T::value (with mapping T > U*) and //  true (with an empty mapping). // No invalid type in mapping even though // T::value is illformed for all pointer types template<typename V> concept C = B<V&>; // Normalizes to the disjunction of //  T::value (with mapping T> V&*) and //  true (with an empty mapping). // Invalid type V&* formed in mapping => illformed NDR
E
is the atomic constraint whose expression is E
and whose parameter mapping is the identity mapping. This includes all fold expressions, even those folding over the &&
or 
operators. Userdefined overloads of &&
or 
have no effect on constraint normalization.
The keyword requires
is used to introduce a requiresclause, which specifies constraints on template arguments or on a function declaration.
template<typename T> void f(T&&) requires Eq<T>; // can appear as the last element of a function declarator template<typename T> requires Addable<T> // or right after a template parameter list T add(T a, T b) { return a + b; }
In this case, the keyword requires must be followed by some constant expression (so it's possible to write requires true
), but the intent is that a named concept (as in the example above) or a conjunction/disjunction of named concepts or a requires expression is used.
The expression must have one of the following forms:
Swappable<T>
, std::is_integral<T>::value
, (std::is_object_v<Args> && ...)
, or any parenthesized expression &&

template<class T> constexpr bool is_meowable = true; template<class T> constexpr bool is_purrable() { return true; } template<class T> void f(T) requires is_meowable<T>; // OK template<class T> void g(T) requires is_purrable<T>(); // error, is_purrable<T>() is not a primary expression template<class T> void h(T) requires (is_purrable<T>()); // OK
Before any further analysis, constraints are normalized by substituting the body of every named concept and every requires expression until what is left is a sequence of conjunctions and disjunctions on atomic constraints.
A constraint P
is said to subsume constraint Q
if it can be proven that P
implies Q
up to the identity of atomic constraints in P and Q. (Types and expressions are not analyzed for equivalence: N > 0
does not subsume N >= 0
).
Specifically, first P
is converted to disjunctive normal form and Q
is converted to conjunctive normal form. P
subsumes Q
if and only if:
P
subsumes every conjunctive clause in the conjunctive normal form of Q
, where U
in the disjunctive clause and an atomic constraint V
in the conjunctive clause such that U
subsumes V
; A
subsumes an atomic constraint B
if and only if they are identical using the rules described above. Subsumption relationship defines partial order of constraints, which is used to determine:
If declarations D1
and D2
are constrained and D1
's associated constraints subsume D2
's associated constraints (or if D2
is unconstrained), then D1
is said to be at least as constrained as D2
. If D1
is at least as constrained as D2
, and D2
is not at least as constrained as D1
, then D1
is more constrained than D2
.
template<typename T> concept Decrementable = requires(T t) { t; }; template<typename T> concept RevIterator = Decrementable<T> && requires(T t) { *t; }; // RevIterator subsumes Decrementable, but not the other way around template<Decrementable T> void f(T); // #1 template<RevIterator T> void f(T); // #2, more constrained than #1 f(0); // int only satisfies Decrementable, selects #1 f((int*)0); // int* satisfies both constraints, selects #2 as more constrained template<class T> void g(T); // #3 (unconstrained) template<Decrementable T> void g(T); // #4 g(true); // bool does not satisfy Decrementable, selects #3 g(0); // int satisfies Decrementable, selects #4 because it is more constrained template<typename T> concept RevIterator2 = requires(T t) { t; *t; }; template<Decrementable T> void h(T); // #5 template<RevIterator2 T> void h(T); // #6 h((int*)0); // ambiguous
Featuretest macro  Value  Std  Comment 

__cpp_concepts  201907L  (C++20)  Constraints 
202002L  (C++20)  Conditionally trivial special member functions 
The following behaviorchanging defect reports were applied retroactively to previously published C++ standards.
DR  Applied to  Behavior as published  Correct behavior 

CWG 2428  C++20  could not apply attributes to concepts  allowed 
Requires expression(C++20)  yields a prvalue expression of type bool that describes the constraints 
© cppreference.com
Licensed under the Creative Commons AttributionShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/constraints