需要区分typename和class的情况
当我们写如下代码的时候:
template<class T>
class kkk {
T::t i;//编译器不知道这个t是一个类型还是对象成员,所以不敢直接定义i,报错
typename T::t i;// typename就是明确告诉编译器这里是类型,等模板实例化再去找
T i;//这样是没有问题的,因为已经在模板中声明了T是一个类类型
};
直接使用auto来定义变量基本不会有这种问题
非类型模板参数
在template中除了类对象和类型,还可以搞一个非类型模板参数,一般用在array里面,用于指定类中的静态数组的大小
注意:这个参数只接受整形,浮点数、类对象以及字符串不允许作为非类型模板参数的
template<class T1,typename T2,size_t n>
class array {
private:
int arr[n];
};
模板的特化
一般模板就是用来接收不确定的类型的,但是假如这个函数需要特别处理的(比如比大小时传过去指针,然而预期比较的是指针解引用后元素的大小而非地址的高低),就需要用到模板的特化
函数模板的特化:
见下面代码
template<typename T>
bool Greater(T p1,T p2) {
return p1 < p2;
}
template<>
bool Greater<int*>(int* p1, int* p2) {
//当调用Greater函数时,参数为int*时,会调用这个函数而非第一个
cout<<"*p1>*p2"<<endl;
}
int main() {
int a = 2, b = 3;
Greater(&a, &b);//输出:*p1>*p2
return 0;
}
然而实现相同的功能,完全可以写成重载的形式,这种方式更加简介,所以一般不推荐写函数的模板特化
bool Greater(int* p1, int* p2) {
return *p1 > *p2;
}
类模板的特化
模板的全特化:
当需要特化的全部参数类型已知的时候,写成如下形式
template<typename T1, typename T2>
class Less {
bool operator()(T1) {
return *T1 > *T2;
}
};
template<>
//这里的尖括号说明不确定的类型的,而下面的括号就是用来指出哪些类型需要特殊匹配的
//由于全特化需要特殊匹配的类型全部已知(int和char),所以直接下面写明即可
class Less<int,char> {
bool operator()() {
cout << "int and char" << endl;
return 0;
}
};
类模板的偏特化:分为部分参数特化和参数模式特化
部分参数特化
固定部分模板参数,让剩余参数保持通用
template<class T1,class T2,class T3>
class test {
test() {
cout<<"T1,T2,T3"<<endl
}
};
template<class T1>//这里T1是不确定的
class test<T1,int,char> {
test() {
cout << "T1,int,char" << endl
}
};
int main(){
test(int ,int ,char);//输出T1,int,char
}
注意:偏特化上下尖括号内的顺序不一定一致,上面说明有哪几个不确定的类型,下面的尖括号从上面的尖括号里取来用而已
参数模式特化
固定具体类型,而是对参数形式添加约束(如指针、引用、数组等)
template<typename T>
class dssa {
public:
dssa() {
cout << "this time is normal template" << endl;
}
};
template<typename T>
class dssa<T*> {//假如传递的是T类型的指针的时候会触发
public:
dssa() {
cout << "this time is T*"<<endl;
}
};
int main() {
dssa<int*> miao;
//输出:this time is T*
return 0;
}
为什么传一个int不会匹配到第一个模板呢?
两个都匹配时:C++规定偏特化更优先
混合使用上述两种方式:
template<typename T1, typename T2>
class kkk {
public:
kkk() {
cout << "this time is normal template" << endl;
}
};
template<typename T1>
class kkk<T1*,int> {
public:
kkk() {
cout << "this time is normal template" << endl;
}
};
模板参数的分离编译
如果是普通类,其成员函数的声明和定义是可以分开的;而模板类在编译过程中,由于声明和定义是分不同文件编译的,当编译看到模板类函数的定义时,因不知道T是什么类型,不会对定义里面的代码实例化,调用该函数时,无法找到对应代码,报错。见下面示例代码:
//test.h
template<class T>
class lll {
bool less(T p1,T p2);
};
//test.cpp
template<class T>
bool lll<T>::less(T p1,T p2) {
return p1 < p2;
}//不知道T是谁,那就不实例化这段代码
//main.cpp
#include "test.h"//include的时候发生的是代码的替换
//下面这段代码被引入
template<class T>
class lll {
bool less(T p1,T p2);//由于定义的代码没有被实例化,这里引入的其实是一具空壳
};
//
int main(){
lll<int>::less(1,2);//调用的时候发现没有代码的实现,报错
return 0;
}
解决方式
显示实例化
直接在类里面实例化一个特定的类型,但这种方式,每一个不同的类型都要实例化一次,非常冗杂
在上面代码的test文件中
//test.cpp
template<class T>
bool lll<T>::less(T p1,T p2) {
return p1 < p2;
}
template class lll<int>;//生成lll的int版本方法
#include "test.h"
int main(){
lll<int>::less(1,2);//想要调用Int版本,链接到了test.cpp中生成的方法,不报错
return 0;}
在同一个文件中进行声明和定义的分离
推荐使用这种方法,写代码的时候注意一下就好了
//都位于一个文件中,就不会出事
template<class T>
class lll {
bool less(T p1,T p2);
};
template<class T>
bool lll<T>::less(T p1,T p2) {
return p1 < p2;
}
模板总结
使用模板的优点
1. 模板复用了代码,节省资源,更快的迭代开发
2. 增强了代码的灵活性
使用模板的缺点
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误
1040

被折叠的 条评论
为什么被折叠?



