明确指定(产生)某template特化体的POI也是可能的,称为明确实例化指令。语法上规定,明确实例化指令由关键词template及待实例化之特化体的声明构成。
例如:
template <typename T>
void f(T) throw(T)
{
}
//valid explicit instantiation commands:
template void f<int>(int) throw(int)
template void f<>(float) throw(float)
template void f(long) throw(long)
template void f(char);
注意,上面的所有实例化指定都是合法的。template argument可被编译器推导而出,异常规格exception specification 可省略。如果它们没有被省略,就必须和template的那一个匹配。
class template的成员也可以这么明确实例化:
template <typename T>
class S
{
public:
void f()
{
}
}
template void S<int>::f();
template class S<void>;
不仅如此,明确实例化一个class template特化体,会使其所有成员都被明确实例化。
C++标准指出,在一个程序中,每个template特化体最多只能有一次明确实例化。而且如果一个template特化体被明确实例化(explicit instantiated),就不能被明确特化(explicit specialized);反之亦然。
首先考虑一个库实现者发布了某个function template的第一版:
//toast.hpp文件
template <typename T>
void toast (T const& x)
{
...
}
客户端程序代码可以含入这个头文件,并明确实例化这个template:
//客户端代码
#include "toast.hpp"
template void toast(float);
不幸的是,如果库作者也将toast<float>明确实例化,便会使得客户端程序代码非法。如果这个库由不同厂家共同做出来的,更有可能发生问题。一些具现了一些标准template,另一些没有这么做,于是客户端无法以一种可移植的方式来对库组件进行明确实例化。
template显式实例化的第二个挑战是:如何提供编译速度。很多C++程序员都发觉,template自动实例化机制对编译时间有不小的负面影响。一种改善编译速度的技术是:在某个位置上手动实例化某些特定的template特化体,而在其他所有编译单元中不做这些实例化工作。唯一一个具有可移植性并能阻止在其他单元发生实例化的方法是:
不用提供template定义式,除非是在它被明确实例化的编译单元内。例如:
//unit 1
template <typename T> void f(); //无定义:避免在本单元中被实例化
void g()
{
f<int>();
}
//unit 2
template <typename T>
void f()
{
}
template void f<int> (); //手动实例化
void g();
int main()
{
g();
}
这个解法运行良好,但程序员必须拥有提供template 接口之源码文件的控制权。通常程序员无法拥有这种控制权。提供template接口的源码文件无法修改,而且它们通常会提供template定义式。
一个技巧是,在所有编译单元中声明某template特化体(可禁止该特化体被自动实例化),惟有在该特化体被明确实例化的编译单元中例外。为了示范这种方法,修改前面的例子,将编译单元1含入一个template定义式:
//unit 1
template <typename T> void f()
{
}
template <> void f<int> ();
void g()
{
f<int>();
}
//unit 2
template <typename T> void f()
{
}
template void f<int>(); //手动实例化
void g();
int main()
{
g();
}
不幸的是这里假设对一个明确特化之特化体的调用等价于一个匹配的泛型特化体的调用。这个假设并不正确。
某些编译器提供了一个扩展语法,用来向编译器指示:一个template特化体不应该在其编译单元内被实例化化。一个语法形式是:在一个明确实例化指令之前添加关键词extern。
修改编译单元1如下:
//unit 1
template <typename T>
void f()
{
}
extern template void f<int> (); //声明但未定义
void g()
{
f<int> ();
}