ISO/IEC14882:2003之条款14.5——模板声明
14.5 模板声明
1、一个template-id,即后面跟着一个template-argument-list的template-name不应该在一个基本模板声明的声明中被指定。【例:
template<class T1, class T2, int I> class A<T1, T2, I> { }; // error template<class T1, int I> void sort<T1, I> (T1 data[I]); // error
——例结束】【注:然而,这个语法在类模板部分特化中被允许(14.5.4)】
2、对于名字查找和实例化的目的,函数模板的默认实参以及类模板的成员函数的默认实参被认为是定义;每个默认实参是一个独立的定义,它与函数模板定义或任何其它默认实参无关。
14.5.1 类模板
1、一个类模板定义了对一个相关类型的非束缚的集合的布局和操作。【例:一单个类模板List可以提供int列表、float列表以及指向Shapes指针列表的一个公共定义。】
2、【例:一个数组类模板可以如此声明:
template<class T> class Array { T* v; int sz; public: explicit Array(int); T& operator[](int); T& elem(int i) { return v[i]; } // ... };
前缀template<class T>指定了正在声明一个模板,并且一个type-name T将在声明中被使用。换句话说,Array是一个以T作为其形参的参数化类型。
】
3、当一个类模板的一个成员函数、一个成员类、一个静态数据成员、或一个成员模板在类模板定义的外部被定义,那么成员定义该成员定义被定义为一个模板定义,在此模板定义中,这些template-parameter是类模板的template-parameter。在成员定义中所使用的模板形参的名字可能与在类模板定义中所使用的模板形参的名字有所不同。跟在成员定义中的类模板的名字后面的模板形参列表应该以与用在成员的模板形参列表中的相同的次序来命名形参。【例:
template<class T1, class T2> struct A { void f1(); void f2(); }; template<class T2, class T1> void A<T2, T1>::f1() { } // OK template<class T2, class T1> void A<T1, T2>::f2() { } // error
——例结束】
4、在一个类模板的重新声明、部分特化、显式特化或显式实例化中,class-key应该遵循原始类模板声明的同样方式(7.1.5.3)。
14.5.1.1 类模板的成员函数
1、一个类模板的一个成员函数可以被定义在它在被声明的类模板定义的外部。【例:
template<class T> class Array { T* v; int sz; public: explicit Array(int); T& operator[](int); T& elem(int i) { return v[i]; } // ... };
声明了三个函数模板。下标函数可以像这么定义:
template<class T> T& Array<T>::operator[](int i) { if(i<0 || sz<=i) error("Array: range error"); return v[i]; }
——例结束】
2、对于一个类模板的一个成员函数的template-argument由调用此成员函数的对象类型的template-argument来确定。【例:对于Array<T>::operator[]()的template-argument将由Array来确定,而下标操作将应用到此Array上。
Array<int> v1(20); Array<dcomplex> v2(30); v1[3] = 7; // Array<int>::operator[]() v2[3] = dcomplex(7, 8); // Array<dcomplex>::operator[]()
——例结束】
14.5.1.2 类模板的成员类
1、一个类模板的一个成员类可以在它所被声明的类模板定义的外部被定义。【注:类成员必须在其第一次要求一次实例化的使用之前被定义(14.7.1)。比如:
template<class T> struct A { class B; }; A<int>::B* b1; // OK: 要求A被定义,但不要求A::B被定义 template<class T> class A<T>::B { }; A<int>::B b2; // OK: 要求A::B被定义
——注结束】
14.5.1.3 类模板的静态数据成员
1、一个静态数据成员的一个定义可以在封闭在静态成员类模板的定义中的一个名字空间作用域中被提供。【例:
template<class T> class X { static T s; }; template<class T> T X<T>::s = 0;
——例结束】
14.5.2 成员模板
1、一个模板可以在一个类或类模板内被声明;这样的一个模板被称为一个成员模板。一个成员模板可以被定义在其类定义或类模板定义的内部或外部。在其类模板外部所定义的一个类模板的一个成员模板应该用类模板的template-parameter,后面跟着成员模板的template-parameter来指定。[例:
template<class T> class string { public: template<class T2> int compare(const T2&); template<class T2> string(const string<T2>& s) { /* ... */ } // ... }; template<class T> template<class T2> int string<T>::compare(const T2& s) { // ... }
——例结束]
2、一个局部类不该有成员模板。访问控制规则(条款11)应用于模板成员名。一个析构器不该有一个成员模板。一个带有给定名字和类型的正常的(非模板)成员函数与一个相同名字的成员函数模板,而这可以被用于生成相同类型的一个特化,可以在一个类中被同时声明。当同时存在时,那个名字以及类型的一个使用引用非模板成员,除非一个显式的模板实参列表被提供。[例:
template <class T> struct A { void f(int); template <class T2> void f(T2); }; template <> void A(int)::f(int) { } template <> template <> void A<int>::f<>(int) { } int main() { A<char> ac; ac.f(1); // 非模板 ac.f('c'); // 模板 ac.f<>(1); // 模板 }
——例结束]
3、一个成员函数模板不应该是虚拟的。[例:
template <class T> struct AA { template <class C> virtual void g(C); // error virtual void f(); // OK };
——例结束]
4、成员函数模板的一个特化并不重写来自一个基类的一个虚函数。[例——
class B { virtual void f(int); }; class D : public B { template <class T> void f(T); // 不重写B::f(int) void f(int i) { f<>(i); } // 重写调用模板实例的函数 };
——例结束]
5、一个模板转换函数的一个特化以一个非模板转换函数转为同一类型的相同的方式被引用。[例:
struct A { template <class T> operator T*(); }; template <class T> A::operator T*() { return 0; } template <> A::operator char*(){ return 0; } // 特化 template A::operator void*(); // 显式实例化 int main() { A a; int* ip; ip = a.operator int*(); // 显式的对模板操作符A::operator int*()的调用 }
——例结束]
[注:因为显式模板实参列表跟在函数模板名后面,并且因为转换成员函数模板和构造器成员模板函数模板不需要一个函数名来调用,所以没有一个方法为这些函数模板提供一个显式的模板实参列表。]
6、一个模板转换函数的一个特化不被名字查找找到。取而代之的是,采用任一在使用的上下文中可见的模板转换函数。对于每个这样的操作符,如果实参推导成功(14.8.2.3),结果特化似乎被名字查找找到而被使用。
7、在一个派生类中的一个using-declaration不能引用在一个基类中的一个模板转换函数的一个特化。
8、重载解决(13.3.3.2)以及部分次序(14.5.5.2)被用于在多个模板转换函数以及/或非模板转换函数中选择最好的转换函数。
14.5.3 友元
1、 一个类或类模板的一个友元可以是一个函数模板或类模板,一个函数模板或类模板的一个特化,或是一个普通的(非模板的)函数或类。对于不是一个模板声明的一个友元函数声明:
——如果友元的名字是一个限定的或非限定的template-id,那么友元声明引用一个函数模板的一个特化,否则,
——如果友元的名字是一个qualified-id,并且在指定的类或名字空间中找到了一个匹配的非模板函数,那么友元声明引用那个函数,否则,
——如果友元的名字是一个qualified-id,并且一个函数模板的一个匹配特化在指定的类或名字空间中找到,那么友元声明引用函数模板特化,否则,
——名字应该是声明(或重声明)一个普通(非模板)函数的一个unqualified-id。【例:
template<class T> class task; template<class T> task<T>* preempt(task<T>*); template<class T> class task { // ... friend void next_time(); friend void process(task<T>*); friend task<T>* preempt<T>(task<T>*); template<class C> friend int func(C); friend class task<int>; template<class P> friend class frd; // ... };
这里,task类模板的每个特化都将函数next_time作为一个友元;因为process没有显式的template-argument,所以task类模板的每个特化都将一个带有适当类型的函数process作为一个友元,并且该友元不是一个函数模板特化;因为友元preempt有一个显式的template-argument<T>,task类模板的每个特化都将函数模板preempt的合适的特化作为一个友元;并且task类模板的每个特化都将函数模板func作为友元。类似的,task类模板的每个特化都将类模板特化task<int>作为一个友元,并且将类模板frd的所有特化作为友元。——例结束】
2、一个不是一个模板声明的一个友元函数声明,并且在此声明中,友元的名字是一个非限定的template-id,应该引用被声明在最近的封闭的名字空间作用域中的一个函数模板的一个特化。[例:
namespace N { template <class T> void f(T); void g(int); namespace M { template <class T> void h(T); template <class T> void i(T); struct A { friend void f<>(int); // 不良形式的——N::f friend void h<>(int); // OK:M::h friend void g(int); // OK: 新的M::g的声明 friend void i<>(int); // 不良形式的——A::i }; } }
——例结束]
3、一个友元模板可以在一个类或类模板内声明。一个友元函数模板可以在一个类或类模板内定义,但一个友元类模板不能在一个类或类模板中被定义。在这些情况下,友元类或友元函数模板的所有特化是授予友元关系的类或类模板的友元。[例:
class A { template<class T> friend class B; // OK template<class T> friend void f(T) { /* ... */ } // OK };
——例结束]
4、 一个模板友元声明指定了那个模板的所有特化,不管它们是否被显式地实例化(14.7.1),被部分特化(14.5.4),还是被显式特化(14.7.3),都是包含模板友元声明的类的友元。[例:
class X { template<class T> friend struct A; class Y { }; }; template<class T> struct A { X::Y ab; }; // OK template<class T> struct A<T*> { X::Y ab; }; // OK
——例结束]
5、当一个函数在一个类模板中的一个友元函数声明中被定义时,函数被定义在类模板的每个实例化上。函数被定义,即使它从未被使用。在多个声明和定义上的相同的限制,这些应用于非模板函数声明和定义,也应用于隐式定义。[注:如果函数定义对于封闭的类模板的一个给定的特化是不良形式的,那么程序是不良形式的,即便该函数从未被使用。]
6、一个类模板的一个成员可以被声明为一个非模板类的一个友元。在这种情况下,类模板的每个特化的相应成员是准予友元关系的类的一个友元。[例:
template<class T> struct A { struct B { }; void f(); }; class C { template<class T> friend struct A<T>::B; template<class T> friend void A<T>::f(); };
——例结束]
7、[注:一个友元声明可以先声明一个封闭的名字空间作用域的一个成员(14.6.5)。]
8、一个友元模板不应该被声明在一个局部类中。
9、友元声明不应该声明部分特化。[例:
template<class T> class A { }; class X { template<class T> friend class A<T*>; // error };
——例结束]
10、当一个友元声明引用一个函数模板的一个特化时,函数形参声明不应该包含默认实参,内联指定符也不应该被用在这样的一个声明中。[译者注:
template <typename T> inline void func(T a = 0) { } class A { friend void func<int>(int a); // OK friend void func<char>(char a = 0); // error friend void inline func<short>(short a); // OK in LLVM2.0 };
]
14.5.4 类模板部分特化
1、一个基本的类模板声明是指,在这个声明中,类模板名是一个标识符。一个类模板声明是在template-id中命名的类模板的一个部分特化,而在此声明中,类模板名是一个template-id。一个类模板的一个部分特化提供了该模板的一个可替换的定义,当在一个特化中的实参匹配了在部分特化中的实参时,模板的部分特化定义被使用,而不是其基本定义(14.5.4.1)。基本模板应该在那个模板的任一特化之前被定义。如果一个模板被部分特化,那么那个部分特化应该在那个部分特化的第一次使用之前被声明,而那个部分特化将导致一个隐式的实例化发生,在每一个这样的一次使用所发生在的翻译单元中;不需要任何诊断。
2、当一个部分特化在一个输出的模板的实例化内被使用时,并且未指定的模板名在输出模板中是非依赖的,那么该部分特化的一个声明必须在输出模板的定义之前被声明,在包含那个定义的翻译单元中。一个类似的限制应用于显式的特化;见14.7。
3、每个类模板部分特化是一个截然不同的模板,并且要为一个模板的部分特化的成员提供定义(14.5.4.3)。
4、[例:
template<class T1, class T2, int I> class A { } // #1 template<class T, int I> class A<T, T*, I> { } // #2 template<class T1, class T2, int I> class A<T1*, T2, I> { } // #3 template<class T> class A<int, T*, 5> { } // #4 template<class T1, class T2, int I> class A<T1, T2*, I> { }; // #5
第一个声明声明了一个基本的(未被特化的)类模板。第二个和后面的声明声明了基本模板的部分特化。
——例结束]
5、模板形参在尖括号封闭的列表中被指定,它紧跟在关键字template后面。对于部分特化,模板实参列表被显式地写在紧跟着类模板名的后面。对于基本模板,该列表被模板形参列表隐式地描述。特别地,模板实参的次序是它们出现在模板形参列表中的次序。【例:对于上述例子中的基本模板的模板实参列表是<T1, T2, I>。】【注:模板实参列表不应该在基本模板声明中被指定。比如:
template<class T1, class T2, int I> class A<T1, T2, I> { }; // error
——注结束】
6、一个类模板部分特化可以在任一名字空间作用域中被声明或重声明,而其定义可以被定义在那个名字空间作用域中(14.5.1和14.5.2)。【例:
template<class T> struct A { class C { template<class T2> struct B { }; }; }; // A<T>::C::B<T2>的部分特化 template<class T> template<class T2> struct A<T>::C::B<T2*>{ }; A<short>::C::B<int*> absip; // 使用部分特化
】
7、部分特化声明了它们自己不被名字查找发现。甚至,当基本模板名被使用时,任一先前所声明的基本模板的部分特化也被考虑。一个结论是一个引用了一个类模板的using-declaration不对部分特化集做限制,而这些部分特化可以通过using-declaration找到。【例:
namespace N { template<class T1, class T2> class A { }; // 基本模板 } using N::A; // 引用基本模板 namespace N { template<class T> class A<T, T*> { }; // 部分特化 } A<int, int*> a; // 使用部分特化,它通过引用基本模板的using声明找到
】
8、一个非类型实参是不被特化的,如果它是一个非类型形参的名字。所有其它的非类型实参被特化。【译者注:
#include <iostream> using namespace std; template <int N> struct A { enum { VALUE = 0 }; }; template <char M> struct A<M> { enum { VALUE = 1}; }; int main(void) { cout << "value is: " << A<'c'>::VALUE << endl; // VC2010输出1 }
】
9、在一个类模板部分特化的实参列表内,会有下列限制:
——一个部分特化的非类型实参表达式不应该涉及部分特化的一个模板形参,除了当实参表达式是一个简单的identifier。【例:
template<int I, int J> struct A { }; template<int I> struct A<I + 5, I * 2> { }; // error template<int I, int J> struct B { }; template<int I> struct B<I, I> { }; // OK
——例结束】
——对应于一个特化的非类型实参的一个模板形参的类型不应该依赖于特化的一个形参。【例:
template<class T, T t> struct C { }; template<class T> struct C<T, 1>; // error template<int X, int (*array_ptr)[X]> class A { }; int array[5]; template<int X> class A<X, &array> { }; // error
——例结束】
——特化的实参列表不应该与基本模板的隐式实参列表相同。
10、一个特化的模板形参列表不应该含有默认的模板形参值。【注:没有方法可以确认它们能使用哪一个。】【译者注:
template <int N = 'c'> struct A { enum { VALUE = 0 }; }; template <char M = 'c'> // error: 一个部分特化不允许使用默认实参 struct A<M> { enum { VALUE = 1}; };
】
14.5.4.1 类模板部分特化的匹配
1、当一个类模板被用在需要类的一个实例化的上下文中时,有必要确定该实例化是使用基本模板生成还是使用部分特化的其中一个来生成。这通过用部分特化的模板实参列表与类模板特化的模板实参进行匹配来完成。
——如果发现正好有一个匹配的特例化,那么实例化用那个特例化来生成。
——如果发现有多余一个的匹配特化,那么使用部分次序规则(14.5.4.2)来确定其中一个特化是否比其它的更特定。如果没有更特定的特化,那么类模板的使用是有歧义的,该程序是不良形式的。
——如果没有找到匹配,那么实例化由基本模板生成。
2、一个部分特化与一个给定的实际模板实参列表匹配,如果部分特化的模板实参可以从实际的模板实参列表推导出(14.8.2)。【例:
// 译者注:参考14.5.4中第4条的代码例子 A<int, int, 1> a1; // 使用 #1 A<int, int*, 1> a2; // 使用 #2, T是int, I是1 A<int, char*, 5> a3; // 使用 #4, T是char A<int, char*, 1> a4; // 使用 #5, T1是int, T2是char, I是1 A<int*, int*, 2> a5; // 有歧义: 匹配 #3 和 #5
——例结束】
3、 一个非类型模板实参也可以从基本模板的一个非类型形参的一个实际模板实参的值推导出。【例:上述a2的声明。】【译者注:
template<class T1, class T2, int I> class A { }; // #1 template<class T, int I> class A<T, T*, I> { }; // #2 A<int, int*, 1> a2;
在实例化a2时,由于a2带有三个模板实参,因此与基本模板(#1)匹配,而此时,基本模板的非类型形参I对应实参1;而随后又发现模板部分特化(#2)比基本模板更特定,因此选择#2作为生成实例化的特化。而特化#2的非类型模板形参I所对应的a2的实参可以由第一次匹配的基本模板的非类型形参I的实际模板实参值1推导出。
】
4、在一个引用一个类模板特化的类型名中,(比如A<int, int, 1>作为一个类型名)实参列表必须匹配基本模板的模板形参列表。一个特化的模板实参通过基本模板的实参被推导出来。
14.5.4.2 类模板特化的部分次序
1、对于两个类模板部分特化,第一个至少要与第二个一样地特化。如果给定了以下对两个函数模板的重写,那么根据函数模板的次序规则(14.5.5.2),第一个函数模板至少要与第二个一样的特化:
——第一个函数模板必须具有与第一个部分特化一样的模板形参,并且具有一单个函数形参,其类型是带有第一个部分特化的模板实参的一个类模板特化,以及
——第二个函数模板具有与第二个部分特化相同的模板形参并且具有一单个函数形参,其类型是带有第二个部分特化的模板实参的一个类模板特化。
2、【例:
template<int I, int J, class T> class X { }; template<int I, int J> class X<I, J, int> { }; // #1 template<int I> class X<I, I, int> { }; // #2 template<int I, int J> void f(X<I, J, int>); // #A template<int I> void f(X<I, I, int>); // #B
部分特化#2比部分特化#1更特化,因为根据模板次序规则,函数模板#B比函数模板#A更特化。
】
14.5.4.3 类模板特化的成员
1、一个类模板部分特化的一个成员的模板形参列表应该匹配类模板部分特化的模板形参列表。一个类模板部分特化的一个成员的模板实参列表应该匹配类模板部分特化的模板实参列表。一个类模板特化是一个独立的模板。类模板部分特化的成员与基本模板成员无关。在以一个需要一个定义的方式下被使用的类模板部分特化成员应该被定义;基本模板的成员的定义永远不会被用作为一个类模板部分特化的成员的定义。一个类模板部分特化的一个成员的显式特化用与基本模板的一个显式特化相同的方式被声明。【例:
// 基本模板 template<class T, int I> struct A { void f(); }; template<class T, int I> void A<T, I>::f() { } // 类模板部分特化 template<class T> struct A<T, 2> { void f(); void g(); void h(); }; // 类模板部分特化的成员 template<class T> void A<T, 2>::g() { } // 显式特化 template<> void A<char, 2>::h() { } int main() { A<char, 0> a0; A<char, 2> a2; a0.f(); // OK。使用基本模板成员的定义 a2.g(); // OK。使用部分特化成员的定义 a2.h(); // OK。使用显式特化成员的定义 a2.f(); // 不良形式的。对于A<T, 2>,f()没有定义。基本模板在这里没被使用 }
——例结束】
2、如果一个类模板的成员模板被部分特化,那么成员模板部分特化是封闭在类模板范围内的成员模板;如果封闭在类模板的部分被实例化(14.7.1,14.7.2),每个成员模板部分特化的一个声明也被实例化,作为创建类模板特化的成员的一部分。如果基本成员模板为一个给定的封闭在类模板范围内的(隐式的)特化而被显式地特化,那么成员模板的部分特化为封闭在类模板范围内的此特化而被忽略。如果成员模板的一个部分特化为一个给定的封闭在类模板范围内的(隐式的)特化而被显式的特化,那么基本模板以及其其它的部分特化仍然为封闭在类模板内的此特化而被考虑。【例:
template<class T> struct A { template<class T2> struct B { }; // #1 template<class T2> struct B<T2*> { }; // #2 译者注:在类模板A范围内对struct B成员模板的部分特化 }; // 译者注:对struct A的显式特化 template<> template<class T2> struct A<short>::B { }; // #3 A<char>::B<int*> abcip; // 使用#2 A<short>::B<int*> absip; // 使用#3 A<char>::B<int> abci; // 使用#1
——例结束】
14.5.5 函数模板
1、一个函数模板定义了一组无界的相关联的函数。【例:排序函数的一个家族可能会如此声明:
template<class T> class Array { }; template<class T> void sort(Array<T>&);
例结束】
2、一个函数模板可以用其它函数模板以及用正常的(非模板)函数重载。一个正常的函数与一个函数模板不相关(即,它永远不会被考虑为一个特化),即使它具有能潜在地生成一个函数模板特化的相同的名字和类型。【注:也就是说,非模板函数的声明并不仅仅用相同的名字来引导函数模板特化的重载决议。如果这么一个非模板函数被用在一个程序中,它必须被定义;它并不会通过使用函数模板定义而被隐式地实例化。】
14.5.5.1 函数模板重载
1、重载函数模板,以至于两个不同的函数模板特化具有相同的类型是有可能的。【例:
// file1.c template<class T> void f(T*); void g(int *p) { f(p); // 调用f<int>(int*) } // file2.c template<class T> void f(T); void h(int *p) { f(p); // 调用f<int*>(int*) }
——例结束】
2、这种特化是有区别的函数,因而并不违背一次定义规则(3.2)。
3、函数模板特化的签名由函数模板的签名和实际模板实参的签名构成(不管是显式指定还是推导)。
4、一个函数模板的签名由其函数签名、其返回类型以及其模板形参列表构成。模板形参的名字只对于建立模板形参和其它签名之间的联系有重要性。【注:两个不同的函数模板可以有相同的函数返回类型和函数形参列表,即使单独的重载决议不能区分它们。
template<class T> void f(); template<int I> void f(); // OK。重载第一个模板 // 用一个显式的模板实参列表即可区别
——注结束】
5、当引用一个模板形参的一个表达式被用在函数形参列表中或在一个函数模板的声明中的返回类型中时,引用模板形参的表达式是函数模板的签名的一部分。这对于准许在一个翻译单元中的一个函数模板的一个声明要与在另一个翻译单元中的函数模板的另一个声明是有必要的,并相反地,以确保打算用于区分的函数模板不被另一个相连接。【例:
template <int I, int J> A<I+J> f(A<I>, A<J>); // #1 template <int K, int L> A<K+L> f(A<K>, A<L>); // 与#1相同 template <int I, int J> A<I-J> f(A<I>, A<J>); // 与#1不同
——例结束】
6、涉及模板形参的两个表达式,如果包含这两个表达式的两个函数定义会满足一次定义规则(3.2),那么这两个表达式会被认为是等价的,除了被用于命名模板形参的记符(token)可能会不同,只要在一个表达式中被用于命名为一个模板形参的一个记符被在另一个表达式中命名同一模板形参的另一个记符代替。【例:
template <int I, int J> void f(A<I+J>); // #1 template <int K, int L> void f(A<K+L>); // 与#1相同
——例结束】涉及模板形参的并不等价的两个表达式中功能上是等价的,如果对于任意给定的一组模板模板实参,表达式的计算结果为相同的值。
7、两个函数模板是等价的,如果它们在同一作用域中被声明,有相同的名字,有相同的模板形参列表,以及使用上述规则以比较涉及模板形参的表达式而等价的返回类型和形参列表。两个函数模板在功能上是等价的,如果它们是等价的除了涉及到返回类型和形参列表的模板形参的一个或多个表达式。如果一个程序包含功能上等价但并不等价的函数模板的声明,那么程序是不良形式的;不需要任何诊断。
8、【注:这条规则确保了等价声明将与另一个连接,而不需要实现使用奇迹般的效果以保证功能上等价的声明会被对待为有区别的。比如,最后两个声明在功能上等价但会导致一个程序是不良形式的:
// 保证是相同的 template <int I> void f(A<I>, A<I+10>); template <int I> void f(A<I>, A<I+10>); // 保证是不同的 template <int I> void f(A<I>, A<I+10>); template <int I> void f(A<I>, A<I+11>); // 不良形式的,不需要诊断 template <int I> void f(A<I>, A<I+10>); template <int I> void f(A<I>, A<I+1+2+3+4>);
——注结束】
14.5.5.2 函数模板的部分次序
1、如果一个函数模板被重载,那么一个函数模板特化的使用可能会有歧义,因为模板实参推导(14.8.2)可以将函数模板特化与多个函数模板声明相关联。被重载的函数模板声明的部分次序被用于以下上下文,以选择一个函数模板特化引用哪个函数模板:
——在对一个函数模板特化的调用的重载决议期间(13.3.3);
——当一个函数模板特化的地址被采取时;
——当作为一个函数模板特化的一个布置(placement)操作符delete被选择,以匹配一个布置操作符new(3.7.3.2,5.3.4)时;
——当一个友元函数声明(14.5.3)、一个显式的实例化(14.7.2)、或一个显式的特化(14.7.3)引用一个函数模板特化时。
2、