模板特化(也有翻译为模板具体化)(specialization)
??如果把模板函数当作数学归纳法的话,模板特化就是n=常数C的情况。
//模板函数声明
template <typename T>
bool greater(const T &a,const T &b);
int main()
{
return 0;
}
//模板函数定义
template <typename T>
bool greater(const T &a,const T &b)
{
if(a > b)
return true;
else
return false;
}
??如果上面的T是char*类型,那么这种比较是不符合我们要求的,它比较的是地址,char*应该用strcmp。
??如果按照我们最简单的思路的话就是直接将T换成char*,但这样编译却给出了error,说这个特化找不到对应的模板函数。
#include <cstring>
//模板函数声明
template <typename T>
bool greater(const T &a,const T &b);
//特化的模板函数的声明
template<>
bool greater<char*>(const char * &a,const char * &b);
int main()
{
return 0;
}
//模板函数定义
template <typename T>
bool greater(const T &a,const T &b)
{
if(a > b)
return true;
else
return false;
}
//特化的模板函数的定义
template<>
bool greater<char *>(const char * &a,const char * &b)
{
return strcmp(a,b)>0?true:false;
}
??我们模板函数的定义const T &a
,其实也可以是这种写法T const &a
,这两种写法表明const和T分别修饰a,也就是a的类型既是const
又是T
;而不是const T作为一个整体,a的类型是const T
。虽然在这个问题上,这两种理解没有出现语义的误解,但是在上面的例子就出现误解了。
? 根据我们原来的模板函数的定义,我们的目的应该是这样的特化函数的参数,const (char *) &a
(是没有这种写法,只是为了让我们理解方便),也就是说char *是一个整体,和const一起修饰a。const char* &a
却让const char
作为了一个类型,这显然不是我们要的。char* const &a
这才是我们想要的。
#include <cstring>
//模板函数声明
template <typename T>
bool greater(T const &a,T const &b);
//特化的模板函数的声明
template<>
bool greater<char*>(char * const &a,char * const &b);
int main()
{
return 0;
}
//模板函数定义
template <typename T>
bool greater(T const &a,T const &b)
{
if(a > b)
return true;
else
return false;
}
//特化的模板函数的定义
template<>
bool greater<char*>(char * const &a,char * const &b)
{
return strcmp(a,b)>0?true:false;
}
??如果在写类型时,习惯将const放在char等后面,那么写模板特化时,直接代换T就不会出现错误。
??如果在写类型时,习惯将const放在类型char等后面,那么写模板特化时,直接代换T就不会出现错误。
定义与实现分离?
??在传统上,我们总是把定义写在.h文件里,而实现文件写在.cpp文件里,但这在模板里面是否可以,请看下面一个例子。
//swap.h
template <typename T>
void swap(T &a,T &b);
//swap.cpp
template <typename T>
void swap(T &a,T &b)
{
T temp = a;
a = b;
b = temp;
}
//main.cpp
#include "swap.h"
int main()
{
int a,b;
swap(a,b);
//...
return 0;
}
??编译器在编译main.cpp文件时只看到swap的定义而没有实现,所以留空,让连接器去链接swap的实现。而编译器在编译swap.cpp时没有看到模板参数,所以也不会编译,最后便会导致链接错误,即找不到swap
??所以模板函数或者模板类,应该将定义和实现放在.h文件。
显示实例化(explict initialization)
??实例化:一个通过使用具体值替换模板参数,从模板产生的普通类,函数或者成员函数的过程。
??隐式实例化:这是编译器看到模板函数时,在当前文件实现相应的模板参数的实例化。
??显示实例化:就是自己手工让编译器在此文件实现相应的模板参数的实例化。
既然编译器会自动实现实例化,为什么还要我们去手工去让编译器实现实例化呢?请看下面的例子。
//swap.h
template <typename T>
void swap(T &a,T &b)
{
T temp = a;
a = b;
b = temp;
}
//function.h
void function();
//function.cpp
#include "swap.h"
void function()
{
int a,b;
swap(a,b);
}
//main.cpp
#include "swap.h"
#include "function.h"
int main()
{
int a,b;
swap(a,b);
function();
//...
return 0;
}
??为了容易看出,我们暂且用function表示一个用到swap的函数,它也可以是一个类。
??编译器在编译function.cpp时会用void swap
//swap.h
template <typename T>
void swap(T &a,T &b);
//swap
templatee <typename T>
void swap(T &a,T &b)
{
T temp = a;
a = b;
b = temp;
}
//explict_initilization.cpp
#include "swap.cpp"
template void swap<int>(int&,int&);//显示实例化
//function.h
void function();
//function.cpp
#include "swap.h"
void function()
{
int a,b;
swap(a,b);
}
//main.cpp
#include "swap.h"
#include "function.h"
int main()
{
int a,b;
swap(a,b);
function();
//...
return 0;
}
更加详细的介绍可以看:c++模板类(一)理解编译器的编译模板过程
export的用法
??为了解决上面用一个explict_initilization.cpp来管理实例化文件的不便利,c++可以export这个关键词,让编译器和链接器去管理我们上面要解决的事情。