[temp.spec] (original) (raw)
13 Templates [temp]
13.9 Template instantiation and specialization [temp.spec]
13.9.1 General [temp.spec.general]
The act of instantiating a function, a variable, a class, a member of a class template, or a member template is referred to astemplate instantiation.
A function instantiated from a function template is called an instantiated function.
A class instantiated from a class template is called an instantiated class.
A member function, a member class, a member enumeration, or a static data member of a class template instantiated from the member definition of the class template is called, respectively, an instantiated member function, member class, member enumeration, or static data member.
A member function instantiated from a member function template is called an instantiated member function.
A member class instantiated from a member class template is called an instantiated member class.
A variable instantiated from a variable template is called an instantiated variable.
A static data member instantiated from a static data member template is called an instantiated static data member.
An explicit specialization may be declared for a function template, a variable template, a class template, a member of a class template, or a member template.
An explicit specialization declaration is introduced bytemplate<>.
In an explicit specialization declaration for a variable template, a class template, a member of a class template, or a class member template, the variable or class that is explicitly specialized shall be specified with a simple-template-id.
In the explicit specialization declaration for a function template or a member function template, the function or member function explicitly specialized may be specified using a template-id.
[Example 1: template<class T = int> struct A { static int x;};template<class U> void g(U) { } template<> struct A<double> { }; template<> struct A<> { }; template<> void g(char) { } template<> void g<int>(int) { } template<> int A<char>::x = 0; template<class T = int> struct B { static int x;};template<> int B<>::x = 1; — _end example_]
An instantiated template specialization can be either implicitly instantiated ([temp.inst]) for a given argument list or be explicitly instantiated ([temp.explicit]).
A specialization is a class, variable, function, or class member that is either instantiated ([temp.inst]) from a templated entity or is an explicit specialization ([temp.expl.spec]) of a templated entity.
For a given template and a given set oftemplate-arguments,
- an explicit instantiation definition shall appear at most once in a program,
- an explicit specialization shall be defined at most once in a program, as specified in [basic.def.odr], and
- both an explicit instantiation and a declaration of an explicit specialization shall not appear in a program unless the explicit specialization is reachable from the explicit instantiation.
An implementation is not required to diagnose a violation of this rule if neither declaration is reachable from the other.
The usual access checking rules do not apply to names in a declaration of an explicit instantiation or explicit specialization, with the exception of names appearing in a function body, default argument, base-clause, member-specification, enumerator-list, or static data member or variable template initializer.
[Note 1:
In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) can be private types or objects that would normally not be accessible.
— _end note_]
Each class template specialization instantiated from a template has its own copy of any static members.
[Example 2: template<class T> class X { static T s;};template<class T> T X<T>::s = 0; X<int> aa; X<char*> bb;
X<int>has a static membersof typeintandX<char*>has a static membersof typechar*.
— _end example_]
If a function declaration acquired its function type through a dependent type without using the syntactic form of a function declarator, the program is ill-formed.
[Example 3: template<class T> struct A { static T t;};typedef int function(); A<function> a; — _end example_]
13.9.2 Implicit instantiation [temp.inst]
A template specialization E is a declared specializationif there is a reachable explicit instantiation definition ([temp.explicit]) or explicit specialization declaration ([temp.expl.spec]) for E, or if there is a reachable explicit instantiation declaration for E andE is not
- an inline function,
- declared with a type deduced from its initializer or return value ([dcl.spec.auto]),
- a potentially-constant variable ([expr.const]), or
- a specialization of a templated class.
[Note 1:
An implicit instantiation in an importing translation unit cannot use names with internal linkage from an imported translation unit ([basic.link]).
— _end note_]
Unless a class template specialization is a declared specialization, the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program.
[Note 2:
In particular, if the semantics of an expression depend on the member or base class lists of a class template specialization, the class template specialization is implicitly generated.
For instance, deleting a pointer to class type depends on whether or not the class declares a destructor, and a conversion between pointers to class type depends on the inheritance relationship between the two classes involved.
— _end note_]
[Example 1: template<class T> class B { };template<class T> class D : public B<T> { };void f(void*);void f(B<int>*);void g(D<int>* p, D<char>* pp, D<double>* ppp) { f(p); B<char>* q = pp; delete ppp; } — _end example_]
[Example 2: template<class T> class X; X<char> ch; — _end example_]
[Note 3:
Within a template declaration, a local class or enumeration and the members of a local class are never considered to be entities that can be separately instantiated (this includes their default arguments,noexcept-specifiers, and non-static data member initializers, if any, but not their type-constraints or requires-clauses).
As a result, the dependent names are looked up, the semantic constraints are checked, and any templates used are instantiated as part of the instantiation of the entity within which the local class or enumeration is declared.
— _end note_]
The implicit instantiation of a class template specialization causes
- the implicit instantiation of the declarations, but not of the definitions, of the non-deleted class member functions, member classes, scoped member enumerations, static data members, member templates, and friends; and
- the implicit instantiation of the definitions of deleted member functions, unscoped member enumerations, and member anonymous unions.
The implicit instantiation of a class template specialization does not cause the implicit instantiation of default arguments or noexcept-specifier_s_of the class member functions.
[Example 3: template<class T> struct C { void f() { T x; } void g() = delete;}; C<void> c; template<> void C<int>::g() { } — _end example_]
However, for the purpose of determining whether an instantiated redeclaration is valid according to [basic.def.odr] and [class.mem], an instantiated declaration that corresponds to a definition in the template is considered to be a definition.
[Example 4: template<class T, class U> struct Outer { template<class X, class Y> struct Inner;template<class Y> struct Inner<T, Y>; template<class Y> struct Inner<T, Y> { }; template<class Y> struct Inner<U, Y> { }; }; Outer<int, int> outer;
Outer<int, int>::Inner<int, Y> is redeclared at #1b.
(It is not defined but noted as being associated with a definition in Outer<T, U>.)
#2 is also a redeclaration of #1a.
It is noted as associated with a definition, so it is an invalid redeclaration of the same partial specialization.
template<typename T> struct Friendly { template<typename U> friend int f(U) { return sizeof(T); } }; Friendly<char> fc; Friendly<float> ff; — _end example_]
Unless a member of a templated class is a declared specialization, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist or if the existence of the definition of the member affects the semantics of the program; in particular, the initialization (and any associated side effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.
Unless a function template specialization is a declared specialization, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist or if the existence of the definition affects the semantics of the program.
A function whose declaration was instantiated from a friend function definition is implicitly instantiated when it is referenced in a context that requires a function definition to exist or if the existence of the definition affects the semantics of the program.
Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.
[Note 4:
An inline function that is the subject of an explicit instantiation declaration is not a declared specialization; the intent is that it still be implicitly instantiated when odr-used ([basic.def.odr]) so that the body can be considered for inlining, but that no out-of-line copy of it be generated in the translation unit.
— _end note_]
[Example 5: template<class T> struct Z { void f();void g();};void h() { Z<int> a; Z<char>* p; Z<double>* q; a.f(); p->g(); }
Nothing in this example requiresclass Z<double>,Z<int>::g(), orZ<char>::f()to be implicitly instantiated.
— _end example_]
Unless a variable template specialization is a declared specialization, the variable template specialization is implicitly instantiated when it is referenced in a context that requires a variable definition to exist or if the existence of the definition affects the semantics of the program.
A default template argument for a variable template is implicitly instantiated when the variable template is referenced in a context that requires the value of the default argument.
The existence of a definition of a variable or function is considered to affect the semantics of the program if the variable or function is needed for constant evaluation by an expression ([expr.const]), even if constant evaluation of the expression is not required or if constant expression evaluation does not use the definition.
[Example 6: template<typename T> constexpr int f() { return T::value; } template<bool B, typename T> void g(decltype(B ? f<T>() : 0));template<bool B, typename T> void g(...);template<bool B, typename T> void h(decltype(int{B ? f<T>() : 0}));template<bool B, typename T> void h(...);void x() { g<false, int>(0); h<false, int>(0); } — _end example_]
If the function selected by overload resolutioncan be determined without instantiating a class template definition, it is unspecified whether that instantiation actually takes place.
[Example 7: template <class T> struct S { operator int();};void f(int);void f(S<int>&);void f(S<float>);void g(S<int>& sr) { f(sr); }; — _end example_]
If a function template or a member function template specialization is used in a way that involves overload resolution, a declaration of the specialization is implicitly instantiated ([temp.over]).
An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class or static data member of a templated class, or a substatement of a constexpr if statement ([stmt.if]), unless such instantiation is required.
[Note 5:
The instantiation of a generic lambda does not require instantiation of substatements of a constexpr if statement within its compound-statementunless the call operator template is instantiated.
— _end note_]
It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated.
The use of a template specialization in a default argument or default member initializer shall not cause the template to be implicitly instantiated except where needed to determine the correctness of the default argument or default member initializer.
The use of a default argument in a function call causes specializations in the default argument to be implicitly instantiated.
Similarly, the use of a default member initializer in a constructor definition or an aggregate initialization causes specializations in the default member initializer to be instantiated.
If a templated functionfis called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function templatefused at that point, except that the scope in which a closure type is declared ([expr.prim.lambda.closure]) — and therefore its associated namespaces — remain as determined from the context of the definition for the default argument.
This analysis is calleddefault argument instantiation.
The instantiated default argument is then used as the argument off.
Each default argument is instantiated independently.
[Example 8: template<class T> void f(T x, T y = ydef(T()), T z = zdef(T()));class A { }; A zdef(A);void g(A a, A b, A c) { f(a, b, c); f(a, b); f(a); } — _end example_]
If such a specifier is needed but has not yet been instantiated, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the specifier is done as if it were being done as part of instantiating the declaration of the specialization at that point.
[Note 6:
[temp.point] defines the point of instantiation of a template specialization.
— _end note_]
There is an implementation-defined quantity that specifies the limit on the total depth of recursive instantiations ([implimits]), which could involve more than one template.
The result of an infinite recursion in instantiation is undefined.
[Example 9: template<class T> class X { X<T>* p; X<T*> a; }; — _end example_]
The type-constraints and requires-clauseof a template specialization or member function are not instantiated along with the specialization or function itself, even for a member function of a local class; substitution into the atomic constraints formed from them is instead performed as specified in [temp.constr.decl] and [temp.constr.atomic]when determining whether the constraints are satisfied or as specified in [temp.constr.decl] when comparing declarations.
[Note 7:
The satisfaction of constraints is determined during template argument deduction ([temp.deduct]) and overload resolution ([over.match]).
— _end note_]
[Example 10: template<typename T> concept C = sizeof(T) > 2;template<typename T> concept D = C<T> && sizeof(T) > 4;template<typename T> struct S { S() requires C<T> { } S() requires D<T> { } }; S<char> s1; S<char[8]> s2;
When S<char> is instantiated, both constructors are part of the specialization.
Their constraints are not satisfied, and they suppress the implicit declaration of a default constructor forS<char> ([class.default.ctor]), so there is no viable constructor for s1.
— _end example_]
[Example 11: template<typename T> struct S1 { template<typename U> requires false struct Inner1; };template<typename T> struct S2 { template<typename U> requires (sizeof(T[-(int)sizeof(T)]) > 1) struct Inner2; };
The class S1<T>::Inner1 is ill-formed, no diagnostic required, because it has no valid specializations.
S2 is ill-formed, no diagnostic required, since no substitution into the constraints of its Inner2 template would result in a valid expression.
— _end example_]
13.9.3 Explicit instantiation [temp.explicit]
A class, function, variable, or member template specialization can be explicitly instantiated from its template.
A member function, member class or static data member of a class template can be explicitly instantiated from the member definition associated with its class template.
The syntax for explicit instantiation is:
There are two forms of explicit instantiation: an explicit instantiation definition and an explicit instantiation declaration.
An explicit instantiation declaration begins with the extern keyword.
An explicit instantiation of a function template, member function of a class template, or variable template shall not use the inline, constexpr, or consteval specifiers.
[Example 1: template<class T> class Array { void mf(); };template class Array<char>;template void Array<int>::mf();template<class T> void sort(Array<T>& v) { } template void sort(Array<char>&); namespace N { template<class T> void f(T&) { } } template void N::f<int>(int&); — _end example_]
A declaration of a function template, a variable template, a member function or static data member of a class template, or a member function template of a class or class template shall be reachable from any explicit instantiation of that entity.
A definition of a class template, a member class of a class template, or a member class template of a class or class template shall be reachable from any explicit instantiation of that entity unless an explicit specialization of the entity with the same template arguments is reachable therefrom.
If the declarationof the explicit instantiation names an implicitly-declared special member function ([special]), the program is ill-formed.
The declaration in an explicit-instantiation and the declaration produced by the corresponding substitution into the templated function, variable, or class are two declarations of the same entity.
[Note 1:
These declarations need to have matching types as specified in [basic.link], except as specified in [except.spec].
[Example 2: template<typename T> T var = {};template float var<float>; template int var<int[16]>[]; template int *var<int>; template<typename T> auto av = T();template int av<int>; template<typename T> auto f() {} template void f<int>(); — _end example_]
— _end note_]
Despite its syntactic form, the declaration in an explicit-instantiation for a variable is not itself a definition and does not conflict with the definition instantiated by an explicit instantiation definition for that variable.
For a given set of template arguments, if an explicit instantiation of a template appears after a declaration of an explicit specialization for that template, the explicit instantiation has no effect.
Otherwise, for an explicit instantiation definition, the definition of a function template, a variable template, a member function template, or a member function or static data member of a class template shall be present in every translation unit in which it is explicitly instantiated.
[Note 2:
An explicit instantiation of a constrained template needs to satisfy that template's associated constraints ([temp.constr.decl]).
The satisfaction of constraints is determined when forming the template name of an explicit instantiation in which all template arguments are specified ([temp.names]), or, for explicit instantiations of function templates, during template argument deduction ([temp.deduct.decl]) when one or more trailing template arguments are left unspecified.
— _end note_]
An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its direct non-template members that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, provided that the associated constraints, if any, of that member are satisfied by the template arguments of the explicit instantiation ([temp.constr.decl], [temp.constr.constr]), except as described below.
[Note 3:
In addition, it will typically be an explicit instantiation of certainimplementation-dependent data about the class.
— _end note_]
An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is an explicit instantiation definition of only those members that have been defined at the point of instantiation.
An explicit instantiation of a prospective destructor ([class.dtor]) shall correspond to the selected destructor of the class.
If an entity is the subject of both an explicit instantiation declaration and an explicit instantiation definition in the same translation unit, the definition shall follow the declaration.
An entity that is the subject of an explicit instantiation declaration and that is also used in a way that would otherwise cause an implicit instantiationin the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed, no diagnostic required.
[Note 4:
This rule does apply to inline functions even though an explicit instantiation declaration of such an entity has no other normative effect.
This is needed to ensure that if the address of an inline function is taken in a translation unit in which the implementation chose to suppress the out-of-line body, another translation unit will supply the body.
— _end note_]
An explicit instantiation declaration shall not name a specialization of a template with internal linkage.
An explicit instantiation does not constitute a use of a default argument, so default argument instantiation is not done.
[Example 3: char* p = 0;template<class T> T g(T x = &p) { return x; } template int g<int>(int); — _end example_]
13.9.4 Explicit specialization [temp.expl.spec]
An explicit specialization of any of the following:
- function template
- class template
- variable template
- member function of a class template
- static data member of a class template
- member class of a class template
- member enumeration of a class template
- member class template of a class or class template
- member function template of a class or class template
can be declared by a declaration introduced bytemplate<>; that is:
[Example 1: template<class T> class stream;template<> class stream<char> { }; template<class T> class Array { };template<class T> void sort(Array<T>& v) { } template<> void sort<int>(Array<int>&); template<> void sort(Array<char*>&);
Given these declarations, #1 will be used as the definition of streams ofchars; other streams will be handled by class template specializations instantiated from the class template.
Similarly, #2 will be used as the sort function for arguments of type Array<int> and #3 will be used for arguments of typeArray<char*>; otherArraytypes will be sorted by functions generated from the function template.
— _end example_]
A declaration of a function template, class template, or variable template being explicitly specialized shall be reachable from the declaration of the explicit specialization.
[Note 1:
A declaration, but not a definition of the template is needed.
— _end note_]
The definition of a class or class template shall be reachable from the declaration of an explicit specialization for a member template of the class or class template.
[Example 2: template<> class X<int> { }; template<class T> class X;template<> class X<char*> { }; — _end example_]
A member function, a member function template, a member class, a member enumeration, a member class template, a static data member, or a static data member template of a class template may be explicitly specialized for a class specialization that is implicitly instantiated; in this case, the definition of the class template shall be reachable from the explicit specialization for the member of the class template.
If such an explicit specialization for the member of a class template names an implicitly-declared special member function ([special]), the program is ill-formed.
A member of an explicitly specialized class is not implicitly instantiated from the member declaration of the class template; instead, the member of the class template specialization shall itself be explicitly defined if its definition is required.
The definition of the class template explicit specialization shall be reachable from the definition of any member of it.
The definition of an explicitly specialized class is unrelated to the definition of a generated specialization.
That is, its members need not have the same names, types, etc. as the members of a generated specialization.
Members of an explicitly specialized class template are defined in the same manner as members of normal classes, and not using the template<> syntax.
The same is true when defining a member of an explicitly specialized member class.
However, template<> is used in defining a member of an explicitly specialized member class template that is specialized as a class template.
[Example 3: template<class T> struct A { struct B { };template<class U> struct C { };};template<> struct A<int> { void f(int);};void h() { A<int> a; a.f(16); } void A<int>::f(int) { } template<> struct A<char>::B { void f();};void A<char>::B::f() { } template<> template<class U> struct A<char>::C { void f();};template<> template<class U> void A<char>::C<U>::f() { } template<> struct A<short>::B { void f();};template<> void A<short>::B::f() { } template<> template<class U> struct A<short>::C { void f();};template<class U> void A<short>::C<U>::f() { } — _end example_]
If a template, a member template or a member of a class template is explicitly specialized, a declaration of that specialization shall be reachable from every use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.
If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required.
An implicit instantiation is never generated for an explicit specialization that is declared but not defined.
[Example 4: class String { };template<class T> class Array { };template<class T> void sort(Array<T>& v) { } void f(Array<String>& v) { sort(v); } template<> void sort<String>(Array<String>& v); template<> void sort<>(Array<char*>& v); template<class T> struct A { enum E : T;enum class S : T;};template<> enum A<int>::E : int { eint }; template<> enum class A<int>::S : int { sint }; template<class T> enum A<T>::E : T { eT };template<class T> enum class A<T>::S : T { sT };template<> enum A<char>::E : char { echar }; template<> enum class A<char>::S : char { schar }; — _end example_]
The placement of explicit specialization declarations for function templates, class templates, variable templates, member functions of class templates, static data members of class templates, member classes of class templates, member enumerations of class templates, member class templates of class templates, member function templates of class templates, static data member templates of class templates, member functions of member templates of class templates, member functions of member templates of non-template classes, static data member templates of non-template classes, member function templates of member classes of class templates, etc., and the placement of partial specialization declarations of class templates, variable templates, member class templates of non-template classes, static data member templates of non-template classes, member class templates of class templates, etc., can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below.
When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.
Asimple-template-idthat names a class template explicit specialization that has been declared but not defined can be used exactly like the names of other incompletely-defined classes ([basic.types]).
[Example 5: template<class T> class X; template<> class X<int>; X<int>* p; X<int> x; — _end example_]
[Note 2:
An explicit specialization of a constrained template needs to satisfy that template's associated constraints ([temp.constr.decl]).
The satisfaction of constraints is determined when forming the template name of an explicit specialization in which all template arguments are specified ([temp.names]), or, for explicit specializations of function templates, during template argument deduction ([temp.deduct.decl]) when one or more trailing template arguments are left unspecified.
— _end note_]
A function with the same name as a template and a type that exactly matches that of a template specialization is not an explicit specialization ([temp.fct]).
Whether an explicit specialization of a function or variable template is inline, constexpr, constinit, or consteval is determined by the explicit specialization and is independent of those properties of the template.
Similarly, attributes and function-contract-specifier_s_appearing in the declaration of a template have no effect on an explicit specialization of that template.
[Example 6: template<class T> void f(T) { } template<class T> inline T g(T) { } template<> inline void f<>(int) { } template<> int g<>(int) { } template<typename> [[noreturn]] void h([[maybe_unused]] int i);template<> void h<int>(int i) { } — _end example_]
An explicit specialization of a static data member of a template or an explicit specialization of a static data member template is a definition if the declaration includes an initializer; otherwise, it is a declaration.
[Note 3:
The definition of a static data member of a template for which default-initialization is desired can use functional cast notation ([expr.type.conv]):template<> X Q<int>::x; template<> X Q<int>::x (); template<> X Q<int>::x = X();
— _end note_]
A member or a member template of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member or member template is defined in the class template definition.
An explicit specialization of a member or member template is specified using the syntax for explicit specialization.
[Example 7: template<class T> struct A { void f(T);template<class X1> void g1(T, X1);template<class X2> void g2(T, X2);void h(T) { } };template<> void A<int>::f(int);template<class T> template<class X1> void A<T>::g1(T, X1) { } template<> template<class X1> void A<int>::g1(int, X1);template<> template<> void A<int>::g1(int, char); template<> template<> void A<int>::g2<char>(int, char); template<> void A<int>::h(int) { } — _end example_]
A member or a member template may be nested within many enclosing class templates.
In an explicit specialization for such a member, the member declaration shall be preceded by atemplate<>for each enclosing class template that is explicitly specialized.
[Example 8: template<class T1> class A { template<class T2> class B { void mf();};};template<> template<> class A<int>::B<double>;template<> template<> void A<char>::B<char>::mf(); — _end example_]
In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well.
In such an explicit specialization declaration, the keywordtemplatefollowed by atemplate-parameter-listshall be provided instead of thetemplate<>preceding the explicit specialization declaration of the member.
[Example 9: template <class T1> class A { template<class T2> class B { template<class T3> void mf1(T3);void mf2();};};template <> template <class X> class A<int>::B { template <class T> void mf1(T);};template <> template <> template<class T> void A<int>::B<double>::mf1(T t) { } template <class Y> template <> void A<Y>::B<double>::mf2() { } — _end example_]
A specialization of a member function template, member class template, or static data member template of a non-specialized class template is itself a template.
An explicit specialization declaration shall not be a friend declaration.
Default function arguments shall not be specified in a declaration or a definition for one of the following explicit specializations:
- the explicit specialization of a function template;
- the explicit specialization of a member function template;
- the explicit specialization of a member function of a class template where the class template specialization to which the member function specialization belongs is implicitly instantiated.
[Note 4:
Default function arguments can be specified in the declaration or definition of a member function of a class template specialization that is explicitly specialized.
— _end note_]