《C++Template》学习笔记——模板实战

模板是位于宏和普通(非模板)声明之间的一种构造

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的前面.

在应用分离模型的最后,实例化过程需要处理两个位置:模板被实例化的位置和模板定义出现的位置。虽然这两个位置在源代码中看起来是完全分离的,但系统却为这两个位置建立了一些看不见的耦合

上一篇:论如何用C++假装写Java


下一篇:c++中把类的声明和实现分到.h和.cpp文件中去