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<A, A, , A> 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 A 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 )  ∨  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 P 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 P shall be the same number N.
        The normal form of F is the normal form of E Op Op E after substituting in E the respective concept argument of each P.
        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 )  ∧  1 == 2.

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

The associated constraints of #3 are requires (T x) { ++x; } (with mapping ).
— 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]