当前位置: 首页 > news >正文

模板的进阶

目录

非类型模板参数

C++11的静态数组容器-array 

按需实例化

模板的特化

函数模板特化

类模板特化

全特化与偏特化

模板的分离编译

总结


非类型模板参数

基本概念:模板参数类型分为类类型模板参数和非类类型模板参数

  • 类类型模板参数:跟在class 或 typename之后的形参
  • 非类类类型模板参数:用一个常量作为类模板的参数

功能:编译时合理分配大小

#include <iostream>
using namespace std;namespace bit
{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;};
}int main()
{bit::array<int> a1;bit::array<int, 10> a2;bit::array<int, 100> a3;return 0;
}

注意事项: 

1、浮点数、类类型的对象及字符串不允许作为非类类型模板参数

2、非类类型模板参数是在编译时传参,函数参数是在运行时传参

3、函数参数(T 对象1,T 对象2)| 模板参数<class 类型1,class 类型2>

C++11的静态数组容器-array 

array文档:<array> - C++ Reference (cplusplus.com)

#include <iostream>
#include <assert.h>
#include <array>
using namespace std;int main()
{std::array<int, 10> a1;int a2[10];//越界读,检查不出来a2[10];//越界写,抽查,局限多,很多位置查不出来(x86环境下运行a2[15]不报错,a[10] = 1报错)a2[15] = 1;//任意读写越界都能检查出来a1[10];//报错return 0;
}

优点: 可以避免数组越界问题(但实际上这些内容vector就可以做到,array没啥用)

std::vector<int> v1(10,0); 
v1[10]//v1[10]也可以检测出

缺点:会出现栈溢出问题

std::array<int,1000000> a3;//报错
std::vector<int> v2(1000000,0);//正确

按需实例化

include <iostream>
#include <array>
#include <vector>
#include <assert.h>
using namespace std;namespace bit
{template<class T, size_t N = 10>class array{public:T& operator[](size_t index){assert(index < N);size(1);//语法错误,但是不报错return _array[index];}const T& operator[](size_t index)const{assert(index < N);return _array[index];}size_t size()const{return _size;}bool empty()const{return 0 == _size;}private:T _array[N];size_t _size;};
}int main()
{bit::array<int> a1;cout<<a1.empty()<<endl;//不报错a1[1];//增加一个调用a1[1]时报错return 0;
}

运行上述代码后不会报错,即使size(1)是一个语法错误(size函数不需要传参)是因为

  1. 在编译器遇到模板时,预处理阶段后不会直接编译,而是根据模板的“蓝图” + 传入模板的参数类型等内容,将模板进行实例化后才会进行编译阶段(调试时是已经经历了四个阶段的)
  2. 在实例化类模板时会进行按需实例化,即调用哪个成员函数就实例化哪个(调用empty就仅实例化该函数)

没有报错的本质就是没有调用operator[],所以operator[]中的错误不会被检查出来,只有调用才会细致检查语法错误

  • 当然编译器还是会大体框架进行检查,比如少了}多个}之类的错误

模板的特化

基本概念:在原模板类的基础上,针对特殊类型所进行特殊化处理

        通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到

一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板:

#include <iostream>
using namespace std;class Date
{
public:friend ostream& operator<<(ostream& _cout, const Date& d);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);}
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}// 函数模板 -- 参数匹配
template<class 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, 6);cout << Less(d1, d2) << endl; // 可以比较,结果正确(会调用日期类对象的<重载)Date* p1 = &d1;Date* p2 = &d2;//(传入日期类对象的指针进行比较)cout << Less(p1, p2) << endl; // 可以比较,结果错误return 0;
}

        p1指向的d1显然大于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内容,

而比较的是p1和p2指针的地址,因而无法达到预期而错误

函数模板特化

注意事项:

  • 1、必须要现有一个基础的函数模板
  • 2、template后接一个空的尖括号<>
  • 3、函数名后跟一对尖括号,尖括号中指定需要特化的类型
  • 4、函数形参顺序必须要和模板函数的基础参数类型完全相同,若不同则编译器可能报错
#include <iostream>
using namespace std;class Date
{
public:friend ostream& operator<<(ostream& _cout, const Date& d);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);}
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}//函数模板
template<class T>
bool Less(T left, T right)
{cout << "bool Less(T left, T right)" << endl;return left < right;
}特化(但是该特化还是会出错)
//template<>
//bool Less(Date* left, Date* right)
//{
//	cout << "bool Less(T* left, T* right)" << endl;
//	return *left < *right;
//}//不会出错但是不符合特化定义
template<class T>
bool Less(T* left, T* right)
{cout << "bool Less(T* left, T* right)" << endl;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 = new Date(2022, 7, 7);Date* p2 = new Date(2022, 7, 8);cout << Less(p1, p2) << endl; // 可以比较,结果错误int* p3 = new int(3);int* p4 = new int(4);cout << Less(p3, p4) << endl; //可以比较,结果错误return 0;
}

~~依然是哪个类型更匹配就用哪个 ~~

结论:函数模板不建议特化do且函数模板特化的使用场景少

类模板特化

全特化与偏特化

全特化:将模板参数列表中的所有参数都确定化

偏/半特化:将模板参数列表中的部分参数确定化

  • 偏特化又分为:“部分特化” 和“参数更进一步的限制”
#include <iostream>
using namespace std;//Data类模板
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;}
};//偏/半特化(部分特化)
template<class T1>
class Data<T1, char>
{
public:Data(){cout << "Data <T1,char>" << endl;}
};//偏/半特化(对参数进一步限制)
template<class T1,class T2>
class Data<T1*, T2*>
{
public:Data(){cout << "Data <T1*,T2*>" << endl;}
};//偏/半特化(对参数进一步限制)
template<class T1, class T2>
class Data<T1&, T2*>
{
public:Data(){cout << "Data <T1&,T2*>" << endl;}
};int main()
{Data<int, int> d1;Data<int, char> d2;Data<char, char> d3;Data<char*, char*> d4;Data<int*, char*> d5;Data<int*, string*> d6;Data<int&, string*> d7;return 0;
}

匹配机制:有现成的就用现成的,没有现成的就用那个最合适的

注意事项:如果传入的是Data*,特化时的T*就会变成Date*而不是Data**,T*是一个整体

模板的分离编译

基本概念:一个程序/项目由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式

假如有以下场景,模板声明和定义分离,在头文件中进行声明,在源文件中进行定义:

//a.h文件
tempate<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;
}

 22、2:33处

注意事项:模板的声明和定义支持分离,但不支持分离在两个文件(STL库中的模板的定义和分离都是在.h文件中的)

总结

优点:

  1. 复用了代码,节省资源,更快的迭代开发,C++的标准模板库也因此而生
  2. 增强代码灵活性

缺点:

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模板编译错误时,错误信息十分混乱,不易定位

~over~

http://www.lryc.cn/news/340430.html

相关文章:

  • 微服务中Dubbo通俗易懂讲解及代码实现
  • Unity HDRP Release-Notes
  • Chrome将网页保存为PDF的实战教程
  • zotero7+Chat GPT实现ai自动阅读论文
  • STM32外设配置以及一些小bug总结
  • 【数据结构与算法】:10道链表经典OJ
  • Python SQL解析和转换库之sqlglot使用详解
  • NULL—0—nullptr 三者关系
  • Nginx 请求的 匹配规则 与 转发规则
  • OWASP发布10大开源软件风险清单
  • 大学生前端学习第一天:了解前端
  • 公安机关人民警察证照片采集规范及自拍制作电子版指南
  • 使用Python插入100万条数据到MySQL数据库并将数据逐步写出到多个Excel
  • 【备忘录】openssl记录
  • hadoop编程之工资序列化排序
  • OpenXR手部跟踪接口与VIVE OpenXR扩展详细解析
  • 慎投!5本On Hold全被剔除!新增9本SCI/SSCI被除名!4月WOS更新
  • 华为云CodeArts IDE For Python 快速使用指南
  • C# 截图并保存为图片
  • [html]一个动态js倒计时小组件
  • Hive-Sql复杂面试题
  • WPS二次开发系列:WPS SDk功能就概览
  • 华为OD-C卷-结队编程[200分]
  • 连连看游戏页面网站源码,直接使用
  • 在 Kubernetes 1.24 中使用 Docker:配置与应用指南
  • Canvas使用详细教学:从基础绘图到进阶动画再到实战(海报生成、Flappy Bird 小游戏等),掌握绘图与动画的秘诀
  • 【MATLAB 分类算法教程】_2粒子群算法优化支持向量机SVM分类 - 教程和对应MATLAB代码
  • Vue2电商前台项目(三):完成Search搜索模块业务
  • 算法思想总结:链表
  • Android Room 记录一个Update语句不生效的问题解决记录