Files
2025-10-25 03:02:53 +03:00

8.5 KiB
Raw Permalink Blame History

[temp.constr.normal]

13 Templates [temp]

13.5 Template constraints [temp.constr]

13.5.4 Constraint normalization [temp.constr.normal]

1

#

The normal form of an expression E is a constraint that is defined as follows:

  • (1.1)

    The normal form of an expression ( E ) is the normal form of E.

  • (1.2)

    The normal form of an expression E1 || E2 is the disjunction of the normal forms of E1 and E2.

  • (1.3)

    The normal form of an expression E1 && E2 is the conjunction of the normal forms of E1 and E2.

  • (1.4)

    For a concept-id C<A1, A2, …, An> termed CI:

    • (1.4.1)

      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.

    • (1.4.2)

      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 concept A = T::value || true;template concept B = A<U*>;template concept C = B<V&>; Normalization of B's constraint-expression is valid and results inT::value (with the mapping T↦U*) ∨ 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]

  • (1.5)

    For a fold-operator Op ([expr.prim.fold]) that is either && or ||:

    • (1.5.1)

      The normal form of an expression ( ... Op E ) is the normal form of ( E Op ... ).

    • (1.5.2)

      The normal form of an expression ( E1 Op ... Op E2 ) is the normal form of + (1.5.2.1) ( E1 Op ... ) Op E2 if E1 contains an unexpanded pack, or

      • [(1.5.2.2)](#1.5.2.2)
        

E1 Op ( E2 Op ... ) otherwise.

  • (1.5.3)

    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.

    • [(1.5.3.1)](#1.5.3.1)
      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 EN−1 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.

+
      [(1.5.3.2)](#1.5.3.2)
      Otherwise,

the normal form of F is a fold expanded constraint ([temp.constr.fold]) whose constraint is E′ and whose fold-operator is Op.

  • (1.6)

    The normal form of any other expression E is the atomic constraint whose expression is E and whose parameter mapping is the identity mapping.

2

#

The process of obtaining the normal form of aconstraint-expression is callednormalization.

[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]

3

#

[Example 2: template concept C1 = sizeof(T) == 1;template concept C2 = C1 && 1 == 2;template concept C3 = requires { typename T::type; };template concept C4 = requires (T x) { ++x; };

template void f1(U); // #1template void f2(U); // #2template void f3(U); // #3

The associated constraints of #1 aresizeof(T) == 1 (with mapping T↦U) ∧ 1 == 2.

The associated constraints of #2 arerequires { typename T::type; } (with mapping T↦U).

The associated constraints of #3 arerequires (T x) { ++x; } (with mapping T↦U).

— end example]

[Example 3: templateconcept C = true;template<typename T, template concept CT>concept CC = CT;

template<typename U, template<typename, template concept> concept CT>void f() requires CT<U*, C>;templatevoid 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: templateconcept A = true;templateconcept B = A && true; // B subsumes Atemplateconcept C = true;templateconcept D = C && true; // D subsumes Ctemplate<typename T, template concept... CTs>concept all_of = (CTs && ...);

template requires all_of<T, A, C>constexpr int f(T) { return 1; } // #1template requires all_of<T, B, D>constexpr int f(T) { return 2; } // #2static_assert(f(1) == 2); // ok

The normal form of all_of<T, A, C> is the conjunction of the normal forms of A and C.

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

#2 therefore is more constrained than #1.

— end example]

[Example 5: template<typename T, template concept>struct wrapper {};

template<typename... T, template concept... CTs>int f(wrapper<T, CTs>...) requires (CTs && ...); // error: fold expression contains// different kinds of template parameters — end example]