Movatterモバイル変換


[0]ホーム

URL:


cppreference.com
Namespaces
Variants
    Actions

      Constraints and concepts(since C++20)

      From cppreference.com
      <cpp‎ |language
       
       
      C++ language
      General topics
      Flow control
      Conditional execution statements
      Iteration statements (loops)
      Jump statements
      Functions
      Function declaration
      Lambda function expression
      inline specifier
      Dynamic exception specifications(until C++17*)
      noexcept specifier(C++11)
      Exceptions
      Namespaces
      Types
      Specifiers
      constexpr(C++11)
      consteval(C++20)
      constinit(C++20)
      Storage duration specifiers
      Initialization
      Expressions
      Alternative representations
      Literals
      Boolean -Integer -Floating-point
      Character -String -nullptr(C++11)
      User-defined(C++11)
      Utilities
      Attributes(C++11)
      Types
      typedef declaration
      Type alias declaration(C++11)
      Casts
      Memory allocation
      Classes
      Class-specific function properties
      Special member functions
      Templates
      Miscellaneous
       
      Declarations
       
      Expressions
      General
      Literals
      Operators
      Conversions
       
       

      Class templates,function templates (includinggeneric lambdas), and othertemplated functions (typically members of class templates) might be associated with aconstraint , which specifies the requirements on template arguments, which can be used to select the most appropriate function overloads and template specializations.

      Named sets of suchrequirements are calledconcepts . 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:

      Run this code
      #include <cstddef>#include <concepts>#include <functional>#include <string> // 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_ttemplate<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 /* parameter-name */) {} 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 toISO 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.”

      Contents

      [edit]Concepts

      A concept is a named set ofrequirements. The definition of a concept must appear at namespace scope.

      The definition of a concept has the form

      template <template-parameter-list>

      conceptconcept-name attr (optional)=constraint-expression;

      attr - sequence of any number ofattributes
      // concepttemplate<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 definitiontemplate<class T> requires C1<T>concept Error2=true;// Error: the requires clause 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 id-expression. The value of the id-expression istrue if the constraint expression is satisfied, andfalse otherwise.

      Concepts can also be named in a type-constraint, as part of

      In atype-constraint, 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>

      [edit]Constraints

      A constraint is a sequence of logical operations and operands that specifies requirements on template arguments. They can appear withinrequires expressions or directly as bodies of concepts.

      There arethree(until C++26)four(since C++26) types of constraints:

      1) conjunctions
      2) disjunctions
      3) atomic constraints
      4) fold expanded constraints
      (since C++26)

      The constraint associated with a declaration is determined bynormalizing a logical AND expression whose operands are in the following order:

      1. the constraint expression introduced for each constrainedtype template parameter or constant template parameter declared with a constrainedplaceholder type, in order of appearance;
      2. the constraint expression in therequires clause after the template parameter list;
      3. the constraint expression introduced for each parameter with constrainedplaceholder type in anabbreviated function template declaration;
      4. the constraint expression in the trailingrequires clause.

      This order determines the order in which constraints are instantiated when checking for satisfaction.

      [edit]Redeclarations

      A constrained declaration may only be redeclared using the same syntactic form. No diagnostic is required:

      // These first two declarations of f are finetemplate<Incrementable T>void f(T) requires Decrementable<T>; template<Incrementable T>void f(T) requires Decrementable<T>;// OK, redeclaration // Inclusion of this third, logically-equivalent-but-syntactically-different// declaration of f is ill-formed, no diagnostic requiredtemplate<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>;// ill-formed, no diagnostic required

      [edit]Conjunctions

      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 short-circuited (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>constexprbool 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}

      [edit]Disjunctions

      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 short-circuited (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;

      [edit]Atomic constraints

      An atomic constraint consists of an expressionE and a mapping from the template parameters that appear withinE to template arguments involving the template parameters of the constrained entity, called itsparameter mapping .

      Atomic constraints are formed duringconstraint 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 expressionE. If the substitution results in an invalid type or expression, the constraint is not satisfied. Otherwise,E, after any lvalue-to-rvalue conversion, must be a prvalue constant expression of typebool, and the constraint is satisfied if and only if it evaluates totrue.

      The type ofE after substitution must be exactlybool. No conversion is permitted:

      template<typename T>struct S{constexpr operatorbool()const{returntrue;}}; 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 consideredidentical if they are formed from the same expression at the source level and their parameter mappings are equivalent.

      template<class T>constexprbool is_meowable=true; template<class T>constexprbool 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}

      Fold expanded constraints

      Afold expanded constraint is formed from a constraintC and a fold operator (either&& or||). A fold expanded constraint is apack expansion.

      LetN be the number of elements in the pack expansion parameters:

      • If the pack expansion is invalid (such as expanding packs of different size), the fold expanded constraint is not satisfied.
      • IfN is0, the fold expanded constraint is satisfied if the fold operator is&&, or not satisfied if the fold operator is||.
      • For a fold expanded constraint with a positiveN,for eachi in[1N], each pack expansion parameter is replaced with the correspondingith element in increasing order:
      • For fold expanded constraints whose fold operator is&&, if the replacement of thejth element violatesC, the fold expanded constraint is not satisfied. In this case, no substitution takes place for anyi greater thanj. Otherwise, the fold expanded constraint is satisfied.
      • For fold expanded constraints whose fold operator is||, if the replacement of thejth element satisfiesC, the fold expanded constraint is satisfied. In this case, no substitution takes place for anyi greater thanj. Otherwise, the fold expanded constraint is not satisfied.


      template<class T> concept A=std::is_move_constructible_v<T>;template<class T> concept B=std::is_copy_constructible_v<T>;template<class T> concept C= A<T>&& B<T>; // in C++23, these two overloads of g() have distinct atomic constraints// that are not identical and so do not subsume each other: calls to g() are ambiguous// in C++26, the folds are expanded and constraint on overload #2 (both move and copy// required), subsumes constraint on overload #1 (just the move is required)template<class...T>requires(A<T>&& ...)void g(T...);// #1 template<class...T>requires(C<T>&& ...)void g(T...);// #2


      (since C++26)

      [edit]Constraint normalization

      Constraint normalization is the process that transforms a constraint expression into a sequence of conjunctions and disjunctions of atomic constraints. Thenormal form of an expression is defined as follows:

      • The normal form of an expression(E) is the normal form ofE.
      • The normal form of an expressionE1&& E2 is the conjunction of the normal forms ofE1 andE2.
      • The normal form of an expressionE1|| E2 is the disjunction of the normal forms ofE1 andE2.
      • The normal form of an expressionC<A1, A2, ... , AN>, whereC names a concept, is the normal form of the constraint expression ofC, after substitutingA1,A2, ... ,AN forC's respective template parameters in the parameter mappings of each atomic constraint ofC. If any such substitution into the parameter mappings results in an invalid type or expression, the program is ill-formed, 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 ill-formed 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 => ill-formed NDR
      • The normal form of expressions(E&& ...) and(...&& E) is a fold expanded constraint, whereC is the normal form ofE and the fold operator is&&.
      • The normal form of expressions(E|| ...) and(...|| E) is a fold expanded constraint, whereC is the normal form ofE and the fold operator is||.
      • The normal forms of expressions(E1&& ...&& E2) and(E1|| ...|| E2) are the normal forms of
      • (E1&& ...)&& E2 and(E1|| ...)|| E2 respectively, ifE1 contains an unexpanded pack, or
      • E1&&(...&& E2) andE1||(...|| E2) respectively otherwise.
      (since C++26)
      • The normal form of any other expressionE is the atomic constraint whose expression isE and whose parameter mapping is the identity mapping. This includes allfold expressions, even those folding over the&& or|| operators.

      User-defined overloads of&& or|| have no effect on constraint normalization.

      [edit]requires clauses

      The keywordrequires is used to introduce arequires clause , 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 listT add(T a, T b){return a+ b;}

      In this case, the keywordrequires must be followed by some constant expression (so it's possible to writerequirestrue), but the intent is that a named concept (as in the example above) or a conjunction/disjunction of named concepts or arequires expression is used.

      The expression must have one of the following forms:

      • Aprimary expression, e.g.Swappable<T>,std::is_integral<T>::value,(std::is_object_v<Args>&& ...), or any parenthesized expression.
      • A sequence of primary expressions joined with the operator&&.
      • A sequence of aforementioned expressions joined with the operator||.
      template<class T>constexprbool is_meowable=true; template<class T>constexprbool is_purrable(){returntrue;} 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

      [edit]Partial ordering of constraints

      Before any further analysis, constraints arenormalized by substituting the body of every named concept and everyrequires expression until what is left is a sequence of conjunctions and disjunctions on atomic constraints.

      A constraintP is said tosubsume constraintQ if it can be proven thatPimpliesQ up to the identity of atomic constraints in P and Q. (Types and expressions are not analyzed for equivalence:N > 0 does not subsumeN >= 0).

      Specifically, firstP is converted to disjunctive normal form andQ is converted to conjunctive normal form.P subsumesQ if and only if:

      • every disjunctive clause in the disjunctive normal form ofP subsumes every conjunctive clause in the conjunctive normal form ofQ, where
      • a disjunctive clause subsumes a conjunctive clause if and only if there is an atomic constraintU in the disjunctive clause and an atomic constraintV in the conjunctive clause such thatU subsumesV;
      • an atomic constraintA subsumes an atomic constraintB if and only if they are identical using the rules describedabove.
      • A fold expanded constraintA subsumes another fold expanded constraintB if they have the same fold operator, the constraintC ofA subsumes that ofB, and bothC contain an equivalent unexpanded pack.
      (since C++26)

      Subsumption relationship defines partial order of constraints, which is used to determine:

      This section is incomplete
      Reason: backlinks from the above to here

      If declarationsD1 andD2 are constrained andD1's associated constraints subsumeD2's associated constraints (or ifD2 is unconstrained), thenD1 is said to beat least as constrained asD2. IfD1 is at least as constrained asD2, andD2 is not at least as constrained asD1, thenD1 ismore constrained thanD2.

      If all following conditions are satisfied, a non-template functionF1 ismore partial-ordering-constrained than a non-template functionF2:

      • They have the same parameter-type-list, omitting the types ofexplicit object parameters(since C++23).
      • If they are member functions, both are direct members of the same class.
      • If both are non-static member functions, they have the same types for their object parameters.
      • F1 is more constrained thanF2.
      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 #1f((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 #3g(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

      [edit]Notes

      Feature-test macroValueStdFeature
      __cpp_concepts201907L(C++20)Constraints
      202002L(C++20)Conditionally trivialspecial member functions

      [edit]Keywords

      concept,requires,typename

      [edit]Defect reports

      The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

      DRApplied toBehavior as publishedCorrect behavior
      CWG 2428C++20could not apply attributes to conceptsallowed

      [edit]See also

      Requires expression(C++20) yields a prvalue expression of typebool that describes the constraints[edit]
      Retrieved from "https://en.cppreference.com/mwiki/index.php?title=cpp/language/constraints&oldid=182711"

      [8]ページ先頭

      ©2009-2025 Movatter.jp