许昌市做网站,微信小程序入口登录,物业公司网站设计,扁平化手机网站模板在前面的文章中#xff0c;我们已经简单的了解了模板的使用#xff0c;在这篇文章中#xff0c;我们将继续深入探讨模板
1.模板的特化
1.1 概念
通常情况下#xff0c;使用模板可以实现一些与类型无关的代码#xff0c;但对于一些特殊类型的可能会得到一些错误的结果我们已经简单的了解了模板的使用在这篇文章中我们将继续深入探讨模板
1.模板的特化
1.1 概念
通常情况下使用模板可以实现一些与类型无关的代码但对于一些特殊类型的可能会得到一些错误的结果需要特殊处理比如实现一个专门用来进行小于比较的函数模板
#include iostream
using namespace std;
template class T
bool LESS(T left, T right)
{return left right;
}
class date
{
public:date(int year 2025,int month2,int day1):_year(year),_month(month),_day(day){}bool operator(const date d)const{return (_year d._year) ||(_year d._year _month d._month) ||(_year d._year _month d._month _day d._day);}bool operator(const date d)const{return (_year d._year) ||(_year d._year _month d._month) ||(_year d._year _month d._month _day d._day);}
private:int _year;int _month;int _day;
};
int main()
{cout LESS(1, 2) endl; // 可以比较结果正确cout LESS(2, 1) endl; // 可以比较结果正确//日期类自定义类型也可以date d1(2025, 4, 20);date d2(2025, 1, 7);cout LESS(d1, d2) endl;return 0;
}
但是倘若我们要这样比较结果就不一定正确了因为这里我们在比较地址而不是比较内容本身 因此我们要对模板进行特化 使得上述例子得以正确执行
1.2 函数模板特化
函数模板的特化步骤
1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号
3. 函数名后跟一对尖括号尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同如果不同编译器可能会报一些奇怪的 错误。
template class T
bool LESS(T left, T right)
{return left right;
}
template
bool LESSdate*(date* left, date* right)
{return *left *right;
}
int main()
{date d1(2050, 4, 20);date d2(2022, 1, 7);date* p1 d1;date* p2 d2;cout LESS(p1, p2) endl;return 0;
} 这样那个指针的例子便可以通过了因为其将调用特化之后的版本而不走原来的模板了
但是我们之前就学过函数这样在写一个函数模板显得多此一举了不如直接写一个函数还省事
bool LESS(date* d1, date* d2)
{return *d1 *d2;
}
int main()
{date d1(2002, 4, 20);date d2(2022, 1, 7);date* p1 d1;date* p2 d2;cout LESS(p1, p2) endl;return 0;
}
结论函数模板不建议特化。
1.3 类模板特化
1.3.1 全特化
全特化即是将模板参数列表中所有的参数都确定化
//总的模板
template class T1,class T2
class rens
{
public:rens() { cout renst1,t2 endl; }
private:T1 _r1;T2 _r2;
};
//全特化
template
class rensint*, int*
{
public:rens() { cout rensint*,int* endl; }
};
template
class rensint, char
{
public:rens() { cout rensint, char endl; }
//这个私有写不写都行个人不喜欢写
private:int r1;char r2;
};
template
class renschar, int
{
public:rens() { cout renschar,int endl; }
//这个私有写不写都行个人不喜欢写
private:char r1;int r2;
};
int main()
{rensint, char r1;rensint*,int r2;renschar, int r3;rensint*, int* r4;return 0;
}
运行结果如下
结果是这个的原因也不难理解编译器在选择模板来编译时一定回去选择最合适的模板来匹配编译
1.3.2 偏特化
偏特化任何针对模版参数进一步进行条件限制设计的特化版本。
其有两种表现方式一种是将模板参数类表中的一部分参数特化见第一个偏特化例子另一种是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
templateclass t1,class t2
class rens
{
public:rens() { cout renst1,t2 endl; }
private:t1 _d1;t2 _d2;
};
//偏特化,将模板参数类表中的一部分参数特化
templateclass T1
class rensT1, int
{
public:rens(){cout rensT1,int endl;}
//这个东西写不写都行
private:T1 _d1;int _d2;
};
//两个参数偏特化为指针类型
templateclass T1,class T2
class rensT1*, T2*
{
public:rens() { cout rensT1*, T2* endl; }
};
//两个参数偏特化为引用类型
templateclass T1,class T2
class rensT1, T2
{
public:rens() { cout rensT1, T2 endl; }
};
//两个参数偏特化为指针类型和引用类型
templateclass T1, class T2
class rensT1*, T2
{
public:rens() { cout rensT1*,T2 endl; }
};
int main()
{rensdouble, int r1;rensint, double r2;rensint*, int* r3;rensint, intr4;rensint*, intr5;return 0;
}
运行结果 2.模板分离编译
与普通函数不同带有模板参数的函数的声明和定义不能分开!否则会报错 为此我们的解决方案是将带模板参数的函数的声明的定义都放在.h文件中如下图所示 3.应用 比如我现在有一个vector容器现在我们想写一个函数来打印它那应该怎么办呢我们有如下几种方式
一、老老实实的写打印函数
#includevector
#includelistvoid print(const vectorint v)
{vectorint::const_iterator it v.begin(); //参数是const vectorint,所以我们的迭代 器也要用const_iteratorwhile (it ! v.end()){cout *it ;it;}cout endl;
}
int main()
{vectorint v1 { 1,2,3,4,5,6 };print(v1);return 0;
}
这样写优缺点也很明确优点是这种写法很简单看过几遍就会写缺点是过于死板如果再让你打印一下double类型或者其他类型你还得辛辛苦苦去写太浪费时间不过可以适用于摸鱼哈开玩笑请勿当真 为此我们可以在函数里面加一丢丢模板这样我们就不必在写double的打印函数了
二、加模板参数的打印函数
#includevector
template class T
void print(const vectorT v)
{
// typename vectorT::const_iterator it v.begin(); //解释一下这里为什么要加上typename:// 因为vectorT是一个模板类// 编译器可能无法自动推断出vectorT::const_iterator是一个类型// 因此需要typename明确指出auto it v.begin(); //推荐用auto让编译器自动给我们推导类型while (it ! v.end()){cout *it ;it;}cout endl;
}
int main()
{vectorint v1 { 1,2,3,4,5,6 };print(v1);vectordouble v2 { 2.2,5.6,9.6,8.3,4.5 };print(v2);return 0;
}这里有两种方法一个是用typename关键字另外一个就是引入c11的auto类型 使用auto更加便捷和简洁
现在你已经可以打印各种类型的vector了但是倘若现在突然让你打印list容器呢而且我们来不及挨个写了为此我们再改造一下使之得以实现
三、设置一个容器模板
#includevector
#includelist
templateclass container
void print(const container con)
{//typename container::const_iterator it con.begin();auto it con.begin();while (it ! con.end()){cout *it ;it;}cout endl;
}int main()
{vectorint v1 { 1,2,3,4,5,6 };print(v1);vectordouble v2 { 2.2,5.6,9.6,8.3,4.5 };print(v2);listint l1 { 1,5,6,9,8,7 };print(l1);listdouble l2 { 1.1,2.2,3.3 };print(l2);return 0;
}
这样不论我是vectorint,还是listdouble,我都可以通过container模板将其替代回去完成打印的操作
4. 模板总结
【优点】
1. 模板复用了代码节省资源更快的迭代开发C的标准模板库(STL)因此而产生
2. 增强了代码的灵活性
【缺陷】
1. 模板会导致代码膨胀问题也会导致编译时间变长
2. 出现模板编译错误时错误信息非常凌乱不易定位错误