广州做网站新锐,做海报有哪些网站,杭州网站优化方案,crm管理系统在线使用1. 非类型模板参数
模板参数分类: 类型形参 与 非类型形参
类型形参即#xff1a;出现在模板参数列表中#xff0c;跟在class或者typename之类的参数类型名称。
非类型形参#xff0c;就是用一个常量作为类(函数)模板的一个参数#xff0c;在类(函数)模板中可将该参数当…1. 非类型模板参数
模板参数分类: 类型形参 与 非类型形参
类型形参即出现在模板参数列表中跟在class或者typename之类的参数类型名称。
非类型形参就是用一个常量作为类(函数)模板的一个参数在类(函数)模板中可将该参数当成常量来使用。
// 静态数组
// 非类型模板参数 -- 常量
// class T 类型形参
// size_t N 非类型形参
templateclass T, size_t N
class Array
{
private:T _a[N];
};// case 1
int main()
{Arrayint, 10 a1; // 10Arraydouble, 1000 a2; // 1000return 0;
}// case2
// array的本质就是一个数组
int main()
{int a1[10];arrayint, 10 a2;arrayint, 100 a3;// c语言的数组// 对于越界的检查// 越界读不检查// 越界写 抽查cout a1[10] endl; // 越界未检测出错误cout a1[11] endl; // 越界未检测出错误// 分别测试以下两个位置是否会被检测出错误// a1[10] 0; // 越界检测出错误a1[15] 0; // 越界未检测出错误// C11 array// 对于c 越界是一定会被检测出错误的无论是读还是写// 因为array是通过operator[]的重载来访问元素的(每一次都会进行判断而不是抽查)如果越界就会报警告cout a2[10] endl; cout a2[11] endl;// a2[10] 0;// a2[15] 0;return 0;
}注意 浮点数、类对象以及字符串是不允许作为非类型模板参数的。 非类型的模板参数必须在编译期就能确认结果。
// 错误示范
// case1: 用浮点数作为非类型模板参数
templateclass T, double N
void Func(const T x)
{cout N endl;
}// case2:
templateclass T, size_t N
void Func(const T x)
{cout N endl;
}int main()
{int N 100;// 非类型的模板参数必须在编译期就能确认结果// 此处只可以传常量不可以传变量 NFuncint, N(1);return 0;
}
2. 模板的特化
通常情况下使用模板可以实现一些与类型无关的代码但对于一些特殊类型的可能会得到一些错误的结果需要特殊处理比如实现了一个专门用来进行小于比较的函数模板
// 日期类
class Date
{
public:Date(int year 1900, int month 1, int day 1): _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);}friend ostream operator(ostream _cout, const Date d){_cout d._year - d._month - d._day;return _cout;}private:int _year;int _month;int _day;
};// 函数模板 -- 参数匹配
templateclass T
bool Less(T left, T right)
{return left right;
}int main()
{// 可以比较结果正确cout Less(1, 2) endl; Date d1(2022, 7, 7);Date d2(2022, 7, 8);// 可以比较结果正确(日期类内部重载了 和 操作符)cout Less(d1, d2) endl; Date* p1 d1;Date* p2 d2;// 可以比较结果错误,下面有解释cout Less(p1, p2) endl; return 0;
}可以看到Less绝对多数情况下都可以正常比较但是在特殊场景下就得到错误的结果。上述示例中p1指向的d1显然小于p2指向的d2对象但是Less内部并没有比较p1和p2指向的对象内容而比较的是p1和p2指针的地址这就无法达到预期而错误。此时就需要对模板进行特化。即在原模板类的基础上针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特
2. 函数模板特化
函数模板的特化步骤
必须要先有一个基础的函数模板关键字template后面接一对空的尖括号函数名后跟一对尖括号尖括号中指定需要特化的类型函数形参表: 必须要和模板函数的基础参数类型完全相同如果不同编译器可能会报一些奇怪的错误。
// 函数模板 -- 参数匹配
templateclass T
bool Less(T left, T right)
{return left right;
}/*
注意也可以直接给出函数重载来处理而需要去对某些类型进行特殊处理
函数重载 和 函数模板的特化都是可以用的
bool Less(Date* left, Date* right)
{
return *left *right;
}
*/// 对Less函数模板进行特化
// 针对某些类型进行特殊处理 -- Date*
template
bool LessDate*(Date* left, Date* right)
{return *left *right;
}int main()
{cout Less(1, 2) endl; Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout Less(d1, d2) endl; Date* p1 d1;Date* p2 d2;// 调用特化之后的版本而不走模板生成了cout Less(p1, p2) endl; return 0;
}
2.3 类模板特化
2.3.1 全特化
全特化即是将模板参数列表中所有的参数都确定化
// 普通版本
templateclass T1, class T2
class Data
{
public:Data() { cout DataT1, T2 endl; }
private:T1 _d1;T2 _d2;
};// 全特化版本
// 对Data类模板进行全特化
template
class Dataint, char {
public:Data() { cout Dataint, char endl; }
private:int _d1;char _d2;
};void TestVector()
{Dataint, int d1;Dataint, char d2;
}2.3.2 偏特化
偏特化任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类
// 普通版本
templateclass T1, class T2
class Data
{
public:Data() { cout DataT1, T2 endl;}
private:T1 _d1;T2 _d2;
};偏特化有以下两种表现方式
部分特化将模板参数类表中的一部分参数特化
// 将第二个参数特化为int
template class T1
class DataT1, int
{
public:Data() { cout DataT1, int endl; }
private:T1 _d1;int _d2;
};参数更进一步的限制 偏特化并不仅仅是指特化部分参数而是针对模板参数更进一步的条件限制所设计出来的一个特化版本
//两个参数偏特化为指针类型
template typename T1, typename T2
class Data T1*, T2*
{
public:Data() { cout DataT1*, T2* endl; }private:T1 _d1;T2 _d2;
};//两个参数偏特化为引用类型
template typename T1, typename T2
class Data T1, T2
{
public:Data(const T1 d1, const T2 d2): _d1(d1), _d2(d2){cout DataT1, T2 endl;}private:const T1 _d1;const T2 _d2;
};void test2()
{Datadouble, int d1; // 调用偏特化的int版本Dataint, double d2; // 调用普通的模板 Dataint*, int* d3; // 调用特化的指针版本Dataint, int d4(1, 2); // 调用特化的引用版本
}2.3.3 类模板特化应用示例
有如下专门用来按照小于比较的类模板Less
#includevector
#include algorithmtemplateclass T
struct Less
{bool operator()(const T x, const T y) const{return x y;}
};int main()
{Date d1(2022, 7, 7);Date d2(2022, 7, 6);Date d3(2022, 7, 8);vectorDate v1;v1.push_back(d1);v1.push_back(d2);v1.push_back(d3);// 直接排序结果是日期升序sort(v1.begin(), v1.end(), LessDate());vectorDate* v2;v2.push_back(d1);v2.push_back(d2);v2.push_back(d3);// 直接排序结果错误// 此处需要在排序过程中让sort比较v2中存放地址指向的日期对象// 但是走Less模板sort在排序时实际比较的是v2中指针的地址因此无法达到预期sort(v2.begin(), v2.end(), LessDate*());return 0;
}通过观察上述程序的结果发现对于日期对象可以直接排序并且结果是正确的。但是如果待排序元素是指针结果就不一定正确。因为sort最终按照Less模板中方式比较所以只会比较指针而不是比较指针指向空间中内容此时可以使用类版本特化来处理上述问题
// 对Less类模板按照指针方式特化
template
struct LessDate*
{bool operator()(Date* x, Date* y) const{return *x *y;}
};// 特化之后在运行上述代码就可以得到正确的结果3 模板分离编译
3.1 什么是分离编译
一个程序项目由若干个源文件共同实现而每个源文件单独编译生成目标文件最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
3.2 模板的分离编译
假如有以下场景模板的声明与定义分离开在头文件中进行声明源文件中完成定义
// a.h
templateclass T
T Add(const T left, const T right);// a.cpp
templateclass T
T Add(const T left, const T right)
{
return left right;
}// main.cpp
#includea.h
int main()
{Add(1, 2);Add(1.0, 2.0);return 0;
}3.3 解决方法 将声明和定义放到一个文件 xxx.hpp*里面或者xxx.h其实也是可以的。推荐使用这种。 模板定义的位置显式实例化。这种方法不实用不推荐使用
// a.cpp
templateclass T
T Add(const T left, const T right)
{
return left right;
}template class Addint; //模板实例化定义4.模板总结
【优点】
模板复用了代码节省资源更快的迭代开发C的标准模板库(STL)因此而产生增强了代码的灵活性
【缺陷】
模板会导致代码膨胀问题也会导致编译时间变长出现模板编译错误时错误信息非常凌乱不易定位错误