我们在上一节给出的两种方法都可以正常的工作,也完全符合C++标准,然而,标准还给出了另一种机制,导出模版exporting template,这种机制通常也被称为C++模版的分离模型。
6.3. 关键字export
大体上讲,关键字export的功能使用是非常简单的,在一个文件里面定义模版,并在模版的定义和(非定义的)声明的前面加上关键字export, 对于上一节的例子,通过使用export 我们会得到下面的函数模版声明。
#ifndef
#define
//
Export template<typename T>
Void print_typeof(T const &)
#endif
即使在模版定义不可见的条件下,被导出的模版也可以正常使用,换句话说,使用模版的位置和模版定义的位置可以在不同的翻译单元中,在我们的例子中,文件myfirst3_hpp现在只是包含类模版的成员函数的声明,但是相对使用这些成员已经足够了。和刚开始导致编译器报错的那个例子相比,我们只是在代码中添加了关键字export,一切就可以顺利通过了。
在一个预处理文件内部(就是指在一个翻译单元内部),我们只需要在第一个声明前面标记export关键字就可以了,后面的重新声明会隐式的保留这个export特性,这也是我们不需要修改文件myfirst.cpp的原因所在,就是说,myfirst.cpp文件里面的这个定义是隐士exported,因为在它#include的头文件myfirst3.hpp里面,该定义所对应的声明已经被限定为export的了,另一方面,在模版定义中提供了一个冗余的export关键字也是可取的,因为这样可以提高代码的可读性。
实际上关键字export可以应用于函数模版,类模版和成员函数,成员函数模版和类模版的静态数据成员,另外,它还可以用与类模版的声明,这将意味着每个可导出的类成员都被看作导出实体,但是类模版本身实际却没有被导出,因此,类模版的定义仍然需要出现在头文件中,你仍然可以隐式或者显式的定义内联成员函数,然而,内敛函数却是不同可导出的。
Export template<typename T>
Class Myclass
{
public:
void memfunc1();
void memfunc2() {
}
void memfun3();
};
template<typename T>
Inline void Myclass<T>::memfun3()
{
...
}
另外,export关键不能和inline关键字一起使用,如果用于模版的话,export要位于关键字template的前面,譬如下面的程序就是非法的。
template<typename T>
Class Invalid {
Public:
export void wrong(T);
};
Export template<typename T>
Inline void Invalid<T>::wrong(T)
{}
Export template<typename T>
Inline T const & max(T const &a, T const &b)
{
return a < b ?b:a;
}
6.3.2 分离模型的限制
谈到这里,你可能会觉得奇怪,既然导出模版export template可以很好的解决最初的问题,我们为何仍然建议大家使用包含模型呢?事实上,export关键字还有其他一些方面的影响。
首先C++标准推出4年之后,只有一家公司对export关键字支持了。
其次,export看似完美,实际上存在一些缺点。模型分离之后,实例化过程需要处理两个位置,模版被实例化的位置和模版定义出现的位置。类似头文件变化一样,需要都重新编译。