模版进阶,咕咕咕!
1.非类型模版参数
模版参数分类类型形参和非类型形参。
类型形参:出现在模版函数参数列表中,,跟在class/typename之类的参数类型名称。
非类型形参:用一个常量作为类(函数)模版的一个参数,在类(函数)模版中可将参数当成常数使用。
namespace gugu
{
//定义一个模版类型的静态数组
template<class T,size_t N=10>
class array
{
public:
T& operator[](size_t index){return _array[index];}
const T& operator[](size_t index)const{return _array[index];}
size_t size()const{return _size;}
bool empty()const{return 0==_size;}
private:
T _array[N];
size_t _size;
};
}
浮点数,类对象,字符串不允许作为非类型模版参数
非类型模版参数必须在编译器就能确认
2.模版的特化
使用模版可以实现一些与类型无关的代码,对于一些特殊类型的可能会得到错误的结果,要特殊处理。
template<class T>
bool Less(T left,T right)
{
return left<right;
}
int main()
{
cout<<Less(1,2)<<endl;
Date d1(2025,1,1);
Date d2(2025,2,2);
cout<<Less(d1,d2)<<endl;
Date* p1=&d1;
Date* p2=&d2;
cout<<Less(p1,p2)<<endl;//不行,p1指向的d1明显小于d2,但是比较的是指针p1和p2的地址
}
要对模版进行特化,在原模版的基础上,针对特殊类型进行特殊化实现方式
分为函数模版特化和类模版特化。
函数模版特化:
先有一个基础的函数模版
关键字template后面接空的<>
函数名后跟着一对<>,里面有要特化的类型
函数形参表:必须和模版函数的基础函数完全相同,编译器不同可能报错
template<class T>
bool Less(T left,T right)
{
return left<right;
}
template<>
bool Less<Date*>(Date* left,Date* right)
{
return *left<*right;
}
//通常用下面的就行
bool Less(Date* left,Date* right)
{
return *left<*right;
}
类模版特化:
全特化是将模版参数列表中所有的参数都确定化
template<class T1,class T2>
class Data
{
public:
Data(){cout<<"Data<T1,T2>"<<endl;}
private:
T1 d1;
T2 d2;
};template<>
class Data<int,char>
{
public:
Data(){cout<<"Data<int,char>"<<endl;}
private:
int d1;
char d2;
};
偏特化:任何针对模版参数进一步进行条件限制设计的特化版本
template<class T1,class T2>
class Data
{
public:
Data(){cout<<"Data<T1,T2>"<<endl;
private:
T1 d1;
T2 d2;
};
部分特化,将模版参数表中一部分参数特化
template<class T1>
class Data(T1,int>
{
public:
Data(){cout<<"Data<T1,int>"<<endl;
private:
T1 d1;
int d2;
};参数进一步限制,针对整个模版参数更进一步的条件限制
template<typename T1,typename T2>
class Data<T1*,T2*>
{
public:
Data(){cout<<"Data<T1*,T2*>"<<endl;
private:
T1 d1;
T2 d2;
};template<typename T1,typename T2>
class Data<T1&,T2&>
{
public:
Data(){cout<<"Data<T1&,T2&>"<<endl;
private:
const T1& d1;
const T2& d2;
};
template<class T>
struct Less
{
bool operator()(const T& x,const T& y)const
{
return x<y;
}
};vector<Date> v1;
Vector<Date*> v2;
…………………………………………………………之后把实例push_back进去
可以直接排序,日期升序
sort(v1.begin(),v1.end(),Less<Date>());
排列的地址升序,所以要类版本特化
sort(v2.begin(),v2.end(),Less<Date*>()};template<>
struct Less<Date*>
{
bool operator()(Date*x,Date*y)
{
return *x<*y;
}
};
3.模版分离编译
分离编译:一个项目由若干个源文件共同实现,每个源文件单独编译生成目标文件,最后将所有的目标文件连接起来形成单一的可执行文件的过程称为分离编译模式。
//a.h
template<class T>
T Add(const T& left,const T& right);//a.cpp
template<class T>
T Add(const T& left,const T& right)
{
return left+right;
}//main.cpp
#include"a.h"
int main()
{
Add(1,2);
Add(1.0,2.0);
return 0;
}在a.cpp中编译器没有看到对Add模版函数的实例化,因此不会生成具体的加法函数
在main.obj中调用的Add<int>和Add<double>,编译器在链接时才会找它的地址
但是这两个函数没有实例化没有生成具体代码,因此链接时报错将声明和定义放到一个文件.cpp中或者.h中就可以解决了
或者模版定义的位置显示实例化
C/C++程序运行要经历:预处理-编译-汇编-链接
编译:对程序按照语言特性进行词法。语法。语义分析,检查无误后生成汇编代码,头文件不参与编译,编译器对工程中多个源文件是分开编译的。
链接:将多个obj文件合成一个,处理没有解决的地址问题。
4.模版总结
优点:复用了代码,节省资源,更快的迭代开发,(STL)因此产生,增强代码灵活性
缺点:会导致代码膨胀问题,编译时间变长,出现模版编译错误时,错误信息凌乱不易定位