13 Templates [temp]

13.5 Template constraints [temp.constr]

13.5.4 Constraint normalization [temp.constr.normal]

The normal form of an expression E is a constraint that is defined as follows:
  • The normal form of an expression ( E ) is the normal form of E.
  • The normal form of an expression E1 || E2 is the disjunction of the normal forms of E1 and E2.
  • The normal form of an expression E1 && E2 is the conjunction of the normal forms of E1 and E2.
  • For a concept-id C<A1, A2, , An> termed CI:
    • If C names a dependent concept, the normal form of CI is a concept-dependent constraint whose concept-id is CI and whose parameter mapping is the identity mapping.
    • Otherwise, to form CE, any non-dependent concept template argument Ai is substituted into the constraint-expression of C.
      If any such substitution results in an invalid concept-id, the program is ill-formed; no diagnostic is required.
      The normal form of CI is the result of substituting, in the normal form N of CE, appearances of C's template parameters in the parameter mappings of the atomic constraints in N with their respective arguments from C.
      If any such substitution results in an invalid type or expression, the program is ill-formed; no diagnostic is required.
    [Example 1: template<typename T> concept A = T::value || true; template<typename U> concept B = A<U*>; template<typename V> concept C = B<V&>;
    Normalization of B's constraint-expression is valid and results in T​::​value (with the mapping TU*)  ∨  true (with an empty mapping), despite the expression T​::​value being ill-formed for a pointer type T.
    Normalization of C's constraint-expression results in the program being ill-formed, because it would form the invalid type V&* in the parameter mapping.
    — end example]
  • For a fold-operator Op ([expr.prim.fold]) that is either && or ||:
    • The normal form of an expression ( ... Op E ) is the normal form of ( E Op ... ).
    • The normal form of an expression ( E1 Op ... Op E2 ) is the normal form of
      • ( E1 Op ... ) Op E2 if E1 contains an unexpanded pack, or
      • E1 Op ( E2 Op ... ) otherwise.
    • The normal form of an expression F of the form ( E Op ... ) is as follows:
      If E contains an unexpanded concept template parameter pack, it shall not contain an unexpanded template parameter pack of another kind.
      Let E be the normal form of E.
      • If E contains an unexpanded concept template parameter pack Pk that has corresponding template arguments in the parameter mapping of any atomic constraint (including concept-dependent constraints) of E, the number of arguments specified for all such Pk shall be the same number N.
        The normal form of F is the normal form of E0 Op Op EN1 after substituting in Ei the respective ith concept argument of each Pk.
        If any such substitution results in an invalid type or expression, the program is ill-formed; no diagnostic is required.
      • Otherwise, the normal form of F is a fold expanded constraint ([temp.constr.fold]) whose constraint is E and whose fold-operator is Op.
  • The normal form of any other expression E is the atomic constraint whose expression is E and whose parameter mapping is the identity mapping.
The process of obtaining the normal form of a constraint-expression is called normalization.
[Note 1: 
Normalization of constraint-expressions is performed when determining the associated constraints ([temp.constr.constr]) of a declaration and when evaluating the value of an id-expression that names a concept specialization ([expr.prim.id]).
— end note]
[Example 2: template<typename T> concept C1 = sizeof(T) == 1; template<typename T> concept C2 = C1<T> && 1 == 2; template<typename T> concept C3 = requires { typename T::type; }; template<typename T> concept C4 = requires (T x) { ++x; }; template<C2 U> void f1(U); // #1 template<C3 U> void f2(U); // #2 template<C4 U> void f3(U); // #3
The associated constraints of #1 are sizeof(T) == 1 (with mapping TU)  ∧  1 == 2.

The associated constraints of #2 are requires { typename T​::​type; } (with mapping TU).

The associated constraints of #3 are requires (T x) { ++x; } (with mapping TU).
— end example]
[Example 3: template<typename T> concept C = true; template<typename T, template<typename> concept CT> concept CC = CT<T>; template<typename U, template<typename, template<typename> concept> concept CT> void f() requires CT<U*, C>; template<typename U> void g() requires CC<U*, C>;
The normal form of the associated constraints of f is the concept-dependent constraint CT<T, C>.

The normal form of the associated constraints of g is the atomic constraint true.
— end example]
[Example 4: template<typename T> concept A = true; template<typename T> concept B = A<T> && true; // B subsumes A template<typename T> concept C = true; template<typename T> concept D = C<T> && true; // D subsumes C template<typename T, template<typename> concept... CTs> concept all_of = (CTs<T> && ...); template<typename T> requires all_of<T, A, C> constexpr int f(T) { return 1; } // #1 template<typename T> requires all_of<T, B, D> constexpr int f(T) { return 2; } // #2 static_assert(f(1) == 2); // ok
The normal form of all_of<T, A, C> is the conjunction of the normal forms of A<T> and C<T>.

Similarly, the normal form of all_of<T, B, D> is the conjunction of the normal forms of B<T> and D<T>.

#2 therefore is more constrained than #1.
— end example]
[Example 5: template<typename T, template<typename> concept> struct wrapper {}; template<typename... T, template<typename> concept... CTs> int f(wrapper<T, CTs>...) requires (CTs<T> && ...); // error: fold expression contains // different kinds of template parameters — end example]
Morty Proxy This is a proxified and sanitized view of the page, visit original site.