模板
模板介绍
模板定义以关键字template
开始,后面跟一个模板参数列表,这是一个以逗号分隔的一个或者多个模板参数的列表
可以将模板看作是编译器函数
模板分类
函数模板
对类型可以参数化
template<typename T>
void swap(T &a, T &b) {
T tmp;
tmp = a;
a = b;
}
类模板
template <typename T>
class compare
{
private:
T _val;
public:
explicit compare(T & val) : _val(val) { }
explicit compare(T && val) : _val(val) { }
bool operator==(T & t)
{
return _val == t;
}
};
底层原理
template<typename T>
struct Foo
{
T bar;
void doSomething(T param) {/* do stuff using T */}
};
// somewhere in a .cpp
Foo<int> f;
如上例子所示,当编译器读到Foo<int>,编译器会创建一个新类,相当于如下
struct FooInt
{
int bar;
void doSomething(int param) {/* do stuff using int */}
}
关键字template告诉编译器,将要定义一个模板
在函数调用点,编译器用用户指定的类型,从原模版实例化一份函数代码出来
bool compare<int> (int a, int b) {
return a > b;
}
模板实例化(显式实例化 、 隐式实例化)后,编译器会把所有的类参数T换成实例化的类型
C++中模板定义与声明为什么放在同一个文件中
首先明确的是:
对普通函数来说,声明放在头文件中,定义放在源文件中,其它的地方要使用该函数时,仅需要包含头文件即可,因为编译器编译时是以一个源文件作为单元编译的,当它遇到不在本文件中定义的函数时,若能够找到其声明,则会将此符号放在本编译单元的外部符号表中,链接的时候自然就可以找到该符号的定义了。
对于模板。先明确,模板函数是在编译器遇到使用模板的代码时才将模板函数实例化(之前只有函数的模板定义并没有根据函数的使用实现其具体定义)的。若将模板函数声明放在tem.h,模板定义放在tem.cpp,在main.cpp中包含头文件,调用add,按道理说应该实例化int add(int,int)函数,即生成add函数的相应代码,但是此时仅有声明,找不到定义(这里就是需要生成函数的定义),因此此时,它只会实例化函数的符号,并不会实例化函数的实现,即这个时候,在main.o编译单元内,它只是将add函数作为一个外部符号,这就是与普通函数的区别,对普通函数来说,此时的add函数已经由编译器生成相应的代码了,而对模板函数来说,此时并没有生成add函数对应的代码。此时编译main.cpp单元不会报错,但链接就会出现add函数未定义的错误。
其实很明显,明确一点就可以了,即编译器只要遇到使用模板函数时就会实例化相应的函数,若在此编译单元内没有模板函数的定义,它当然不能够实例化成功了。因此通常情况下模板函数的声明与定义均放在同一文件内,因此这样就保证了在使用模板的地方一定可以实例化成功了。