模板是位于宏和普通(非模板)声明之间的一种构造
1、包含类型
1.1、链接器错误
一般情况下,我们会这样组织非模板代码:
(1)类的声明放在头文件中
(2)全局变量和(非内联)函数,只有声明在头文件中,定义在.cpp文件中。
但是对于模板却不能这样做,会产生链接错误,示例如下所示:
cat myfirst.hpp
#ifndef MYFIRST_HPP
#define MYFIRST_HPP
// declaration of template
template<typename T>
void printTypeof (T const&);
#endif // MYFIRST_HPP
cat myfirst.cpp
#include <iostream>
#include <typeinfo>
#include "myfirst.hpp"
// implementation/definition of template
template<typename T>
void printTypeof (T const& x)
{
std::cout << typeid(x).name() << '\n';
}
cat myfirstmain.cpp
#include "myfirst.hpp"
// use of the template
int main()
{
double ice = 3.0;
printTypeof(ice); // call function template for type double
}
编译命令及输出结果如下:
g++ -g -o myfirstmain myfirstmain.cpp
/tmp/ccJn91Ax.o: In function `main':
/home/MyWorkspace/study/myfirstmain.cpp:7: undefined reference to `void printTypeof<double>(double const&)'
collect2: error: ld returned 1 exit status
事实上,这个错误的原因在于:函数模板printTypeof()的定义还没有被实例化。为了使模板真正得到实例化,编译器必须知道:应该实例化哪个定义以及要基于哪个模板实参来进行实例化。遗憾的是,这两部分信息位于分开编译的不同文件里面。因此,当我们的编译器看到printTypeof()调用,但还没看到基于double实例化的函数定义的时候,它只是假设在别处提供了这个定义,并产生一个指向该定义的引用。另一方面,当编译器处理文件myfirst.cpp的时候。它并没有指出:编译器必须基于特定实参对所包含的模板定义进行实例化。
1.2、头文件中的模板
对于上面的问题,可以通过把定义和声明放在一起来解决,如下所示:
cat myfirst.hpp
#ifndef MYFIRST_HPP
#define MYFIRST_HPP
#include <iostream>
#include <typeinfo>
template<typename T>
void printTypeof (T const& x)
{
std::cout << typeid(x).name() << '\n';
}
#endif // MYFIRST_HPP
cat myfirstmain.cpp
#include "myfirst.hpp"
// use of the template
int main()
{
double ice = 3.0;
printTypeof(ice); // call function template for type double
}
编译命令如下:
g++ -g -o myfirstmain myfirstmain.cpp
我们称模板的这种组织方式为包含模型,包含模型明显增加了包含头文件myfirst.hpp的开销,这也正是包含模型最大的不足之处。在例子中,主要的开销并不是取决于模板定义本身的大小,而在于模板定义中所包含的那些头文件的大小,在实际应用中,大大增加了编译复杂程序所耗费的时间。
从包含模型得出的另一个结论是:非内联函数模板与“内联函数和宏”有一个很重要的区别,那就是非内联函数模板在调用的位置并不会被展开,而是当他们基于某种类型实例化之后,才产生一份新的(基于该类型的)函数拷贝。
2、显式实例化
显式实例化指示符,由关键字template和紧接其后的我们所需要实例化的实体的声明组成
template void print_typeof<double>(double const&);
3、分离模型
导出模板(exporting template)
3.1关键字export
在一个文件里面定义模板,并在模板的定义和声明的前面加上export关键字,示例如下:
#ifndef MYFIRST_HPP
#define MYFIRST_HPP
export template<typename T> void print_typeof(T const&);
#endif
使用模板的位置和模板定义的位置可以在两个不同的翻译单元中,export关键字不能和inline关键字一起使用,如果用于模板的话,export要位于关键字template的前面.
在应用分离模型的最后,实例化过程需要处理两个位置:模板被实例化的位置和模板定义出现的位置。虽然这两个位置在源代码中看起来是完全分离的,但系统却为这两个位置建立了一些看不见的耦合