首先总结一下,template其实是C++的一种语法糖,本意是去简化程序员的工作(但我认为语法实在有点繁琐,反而加大了开发成本)。
template的运用场景主要是以下部分,假设我们想写一个交换函数swap
void swap(int *a,int *b){
int temp = *a;
*a = *b;
*b = temp;
}
那你这个交换函数只能用在int的数据类型吧,float呢?float就不用交换了吗?所以我们还得写以下代码
void Swap(float *a, float *b){
float temp = *a;
*a = *b;
*b = temp;
}
对吧,然后还有char类型的swap,还有bool类型的类型的swap,全都要写.....此时你一拍桌子,不写了!什么垃圾玩意!
然后你百度一下,发现了有template这玩意。
那用template会不会更方便嘞,那必须的呀,只用写以下代码。
//声明时这样
template<typename T> void Swap(T *a, T *b){
T temp = *a;
*a = *b;
*b = temp;
}
//调用时这样
int a=10,b=20;
swap(&a,&b);
此时,我们就不用针对每种类型都写一遍swap了(template会告诉编译器:这个a是int!这个b也是int!),这就是template的作用。
template其实比较关键的事函数模板和类模板,其他的都是在这些基础上延伸而来的。
函数模板顾名思义,就是template用在函数上
定义的时候吧,咱就这样定义
template<typename T>
void swap(const T*a,const T*b)
{
T temp = *a;
*a = *b;
*b = temp;
}
然后用起来吧,咱这样用,这时候你可以直接调用swap,这个咱叫隐式调用,就是你不用扯着嗓子喊:这个a是int!这个b也是int!的意思,稍后会讲到。
int main()
{
int a=10,b=20;
float c=30,d=40;
swap(&a,&b);
swap(&c,&d);
return 0;
}
如果你想把你的函数声明为inline提高效率,那么参考下面
inline关键字放在template之后,函数类型之前
//正确写法
template<typename T>inline T swap(const T*a,const T*b)
//错误写法
inline template<typename T>T swap(const T*a,const T*b)
template<class Type>class Queue{
public:
Queue();
Type &front();
const Type &front()const;
void push(const Type &);
void pop();
bool empty()const
private:
...
};
和咱的函数模板不同啊,我们的类模板需要加个括号(显式)告诉编译器老子是啥类型。
queue<int>qi;//什么是显式,在定义时加个括号告诉他老子是int就是显式
Queue<vector<double>>qc;
Queue<sting>qs;
非类型模板形参实际上传的就不是类型了,而是传一个具体的数字!
(待补充)
有些时候,我们编写的模板定义并不总是适用于所有类型
template<typename T>
int compare(const T&v1,const T&v2)
{
if(v1<v2)return -1;
if(v1>v2)return 1;
return 0;
}
当这个函数的参数类型是C风格字符串时,这个函数不能正常工作(这个函数将比较两个指针在内存中的相对位置,但没有比较指针指向对象的大小),这时候我们必须提供一个针对C风格字符串的特殊定义,这就是模板特化
特化的形式如下:
template<>
int compare<const char *>(const char *const &v1,const char *const &v2)
{
return strcmp(v1,v2);
}
与任意函数一样,函数模板特化可以声明而无须定义。
template<>
int compare<const char *>(const char *const &,const char *const &);
如果在特化中省略空的模板形参表template<>,那么结果是函数的重载而不是模板特化
int compare(const char *const&,const char *const&);
//声明了该函数的重载版本而不是模板版本
在调用模板的这个特化版本之前,特化的声明必须在作用域中
template<class T>
int compare(const T&t1,const T& t2)
{
...
}
int main()
{
int i=compare("hello","world");
...
}
template<>
int compare<const char *>(const char *const &s1,const char *const &s2)
{
...
}
这个程序有错误,这个函数将会调用未特化的原模板函数
特化出现在对该模板实例的调用之后是错误的
template<> class Queue<const char*>{
public:
void push(const char *);
void pop();{real_queue.pop();}
bool empty()const{return real_queue.empty();}
std::string front(){return real_queue.front();}
const std::string &front()const{return real_queue.front();}
private:
Queue<std::string>real_queue;
};
类模板特化应该与它所特化的模板定义相同的接口,否则当用户试图使用未定义的成员时会感到奇怪
void Queue<const char *>::push(const char*val)
{
return real_queue.push(val);
}
在类特化外部定义成员时,成员之前不能加template<>标记
template<>
void Queue<const char*>::push(const char *const &val)
{
...
}
template<>
void Queue<const char *>::pop()
{
...
}
现在,类类型Queue<const char *>将从通用类模板定义实例化而来,而push和pop函数例外。调用Queue<const char *>对象的push或pop函数时,将调用特化版本;调用其他任意成员时,将从类模板为const char*实例化一个通用版本。
如果类模板有一个以上的模板形参,我们也许想要特化某些模板形参而非全部,使用类模板的部分特化可以做到这一点:
template<class T1,class T2>
class some_template{
//...
};
//部分特化
template <class T1>
class some_template<T1,int>{
//...
}
some_template<int,string>foo;//使用类模板
some_template<string,int>bar;//使用部分特化
1.foo的实例化类型与提供的部分特化类型不匹配,因此,foo的类型必然从通用类模板实例化
2.bar选择部分特化模板来实例化。当声明了部分特化的时候,编译器将为实例化选择最特化的模板定义,当没有部分特化可以使用的时候,就使用通用模板定义。
ps:函数模板是没有部分特化的,如果声明了一个部分特化的函数模板,编译器会报错