类模板和函数模板的声明方式相同。 模板参数列表在类定义/模板定义之前声明。 例如:
// 类模板 template <class T1, class T2> class A{ T1 data1; T2 data2; }; // 函数模板 template <class T> T max(const T lhs, const T rhs){ return lhs > rhs ? lhs : rhs; }
完全专业化
通过完全专业化模板,您可以针对一组特定参数自定义当前模板。 类模板和函数模板都可以完全专业化。 完全专用的模板参数列表应该为空,并且应该给出“模板参数”列表:
// 全特化类模板 template <> class A<int, double>{ int data1; double data2; }; // 函数模板 template <> int max(const int lhs, const int rhs){ return lhs > rhs ? lhs : rhs; }
请注意,类模板的完全特化在类名后给出“模板参数”,但函数模板在函数名后不给出“模板参数”。 这是因为编译器可以从 int max(const int, const int) 的函数签名推断出它是 T max(const T, const T) 的特化。
专门歧义
上面的函数模板不需要指定“模板参数”,因为编译器可以通过函数签名来推断它,但有时这个过程是不明确的:
template <class T> void f(){ T d; } template <> void f(){ int d; }
此时编译器并不知道f()是从f()特化而来的,编译时会报错:
error: no function template matches function template specialization 'f'
这时候我们需要显式指定“模板参数”:
template <class T> void f(){ T d; } template <> void f<int>(){ int d; }
部分专业化
与完全特化类似,部分特化也用于为参数集定制模板,但部分特化模板需要进一步实例化以形成明确的签名。 值得注意的是,函数模板不允许部分特化,这在 C++: Item 25 中有更详细的讨论。此后也声明了部分特化,剩余的“模板形参”和必要的“模板实参”需要给予。 例如:
template <class T2> class A<int, T2>{ ... };
函数模板不允许部分专业化。 以下声明将导致编译错误:
template <class T1, class T2> void f(){} template <class T2> void f<int, T2>(){}
但函数是允许重载的,并且声明另一个函数模板可以取代部分特化的需要:
template <class T2> void f(){} // 注意:这里没有"模板实参"
大多数情况下,函数模板重载可以满足部分函数特化的需要。 std 命名空间是一个例外。 std是一个特殊的命名空间,用户可以在其中专门化模板,但不允许添加模板(事实上,添加任何内容都是被禁止的)。 因此不允许向 std 添加重载函数,更详细的情况在 C++: Item 25 中给出。