#
A template specialization can be referred to by a template-id:
simple-template-id:
template-name < template-argument-list
template-id:
simple-template-id
operator-function-id < template-argument-list
template-name:
identifier
template-argument-list:
template-argument ..
template-argument:
template-argument-name
constant-expression
type-id
braced-init-list
template-argument-name:
nested-name-specifier
2
#
The component name of a simple-template-id, template-id, or template-name is the first name in it.
A < is interpreted as the delimiter of a template-argument-list if either
[Note 1: 
If the name is an identifier, it is then interpreted as a template-name.
The keyword template is used to indicate that a dependent qualified name ([temp.dep.type]) denotes a template where an expression might appear.
— end note]
[Example 1: struct X { template<std::size_t> X* alloc(); template<std::size_t> static X* adjust(); }; template<class T> void f(T* p) { T* p1 = p->alloc<200>(); / error: < means less than T* p2 = p->template alloc<200>(); / OK, < starts template argument list T::adjust<100>(); / error: < means less than T::template adjust<100>(); / OK, < starts template argument list static constexpr std::meta::info r = ^^T::adjust; T* p3 = [:r:]<200>(); / error: < means less than T* p4 = template [:r:]<200>(); / OK, < starts template argument list }} — end example]
When parsing a template-argument-list, the first non-nested >103 is taken as the ending delimiter rather than a greater-than operator.
Similarly, the first non-nested >> is treated as two consecutive but distinct > tokens, the first of which is taken as the end of the template-argument-list and completes the template-id or splice-specialization-specifier.
[Note 2: 
The second > token produced by this replacement rule can terminate an enclosing template-id or splice-specialization-specifier construct or it can be part of a different construct (e.g., a cast).
— end note]
[Example 2: template<int i> class X { /* ... */ }; X< 1>2 > x1; / syntax error X<(1>2)> x2; / OK template<class T> class Y { /* ... */ }; Y<X<1> x3; / OK, same as Y<X<1> > x3; Y<X<6>1> x4; / syntax error Y<X<(6>1)> x5; / OK — end example]
The keyword template shall not appear immediately after a declarative nested-name-specifier ([expr.prim.id.qual]).
The constant-expression of a template-argument shall not be an unparenthesized splice-expression.
[Example 3: template<int> struct S { }; constexpr int k = 5; constexpr std::meta::info r = ^^k; S<[:r:]> s1; / error: unparenthesized splice-expression used as template argument S<([:r:])> s2; / OK S<[:r:] + 1> s3; / OK — end example]
A name prefixed by the keyword template shall be followed by a template argument list or refer to a class template or an alias template.
The latter case is deprecated ([depr.template.template]).
The keyword template shall not appear immediately before a ~ token (as to name a destructor).
[Note 3: 
The keyword template cannot be applied to non-template members of class templates.
— end note]
[Note 4: 
As is the case with the typename prefix, the template prefix is well-formed even when lookup for the name would already find a template.
— end note]
[Example 4: template <class T> struct A { void f(int); template <class U> void f(U); }; template <class T> void f(T t) { A<T> a; a.template f<>(t); / OK, calls template a.template f(t); / error: not a template-id } template <class T> struct B { template <class T2> struct C { }; }; / deprecated: T​::​C is assumed to name a class template: template <class T, template <class X> class TT = T::template C> struct D { }; D<B<int> > db; — end example]
The component names of a template-argument-name are those of its nested-name-specifier (if any) and its identifier.
A template-id or splice-specialization-specifier is valid if
A simple-template-id or splice-specialization-specifier shall be valid unless its respective template-name or splice-specifier designates a function template ([temp.deduct]).
[Example 5: template<class T, T::type n = 0> class X; struct S { using type = int; }; using T1 = X<S, int, int>; / error: too many arguments using T2 = X<>; / error: no default argument for first template parameter using T3 = X<1>; / error: value 1 does not match type-parameter using T4 = X<int>; / error: substitution failure for second template parameter using T5 = X<S>; / OK — end example]
When the template-name of a simple-template-id or the splice-specifier of a splice-specialization-specifier designates a constrained non-function template or a constrained template template parameter, and all template-arguments in the simple-template-id or splice-specialization-specifier are non-dependent ([temp.dep.temp]), the associated constraints ([temp.constr.decl]) of the constrained template shall be satisfied ([temp.constr.constr]).
[Example 6: template<typename T> concept C1 = sizeof(T) != sizeof(int); template<C1 T> struct S1 { }; template<C1 T> using Ptr = T*; S1<int>* p; / error: constraints not satisfied Ptr<int> p; / error: constraints not satisfied template<typename T> struct S2 { Ptr<int> x; }; / ill-formed, no diagnostic required template<typename T> struct S3 { Ptr<T> x; }; / OK, satisfaction is not required S3<int> x; / error: constraints not satisfied template<template<C1 T> class X> struct S4 { X<int> x; / ill-formed, no diagnostic required }; template<typename T> concept C2 = sizeof(T) == 1; template<C2 T> struct S { }; template struct S<char[2]>; / error: constraints not satisfied template<> struct S<char[2]> { }; / error: constraints not satisfied — end example]
A concept-id is a prvalue of type bool, and does not name a template specialization.
A concept-id evaluates to true if the concept's normalized constraint-expression ([temp.constr.decl]) is satisfied ([temp.constr.constr]) by the specified template arguments and false otherwise.
[Note 5: 
Since a constraint-expression is an unevaluated operand, a concept-id appearing in a constraint-expression is not evaluated except as necessary to determine whether the normalized constraints are satisfied.
— end note]
[Example 7: template<typename T> concept C = true; static_assert(C<int>); / OK — end example]
103)103)
A > that encloses the type-id of a dynamic_cast, static_cast, reinterpret_cast or const_cast, or which encloses the template-arguments of a subsequent template-id or splice-specialization-specifier, is considered nested for the purpose of this description.

Follow Lee on X/Twitter - Father, Husband, Serial builder creating AI, crypto, games & web tools. We are friends :) AI Will Come To Life!

Check out: eBank.nz (Art Generator) | Netwrck.com (AI Tools) | Text-Generator.io (AI API) | BitBank.nz (Crypto AI) | ReadingTime (Kids Reading) | RewordGame | BigMultiplayerChess | WebFiddle | How.nz | Helix AI Assistant