ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

把值写进类型:C++ 模板进阶的非类型参数、特化与分离编译

把值写进类型:C++ 模板进阶的非类型参数、特化与分离编译 浮世尘弦个人主页⭐个人专栏《C语言》、《数据结构与算法》、《C》非淡薄无以明志非宁静无以致远前言前面我们学习了使用类型模板参数templateclass T编写与类型无关的通用代码但是对于 泛型编程来说光有类型模板参数是不够的。本篇文章就要再次基础上再次增加非类型模板参数模板特化模板的分离编译等内容。帮助你深入理解和上手泛型编程。让我开始今天的学习吧文章目录一非类型模版参数1介绍非类型模版参数2array数组二模版特化1函数模板特化​编辑2全特化和偏特化三模板分离编译1,介绍分离编译2模板的声明和定义为什么不能分离四函数模板的优缺点一非类型模版参数在之前我们学习了类型模版参数templateclass TT可以替代任意类型但是不能替代一个实际的常数在一些场景下我们需要用一个常数来定义容器的容量。于是就引入了非类型模版参数。故模版参数分为类型形参和非类型形参。类型形参在之前的已经讲解过具体可以看我的这一篇文章C 核心进阶一泛型编程基石 —— 模板初阶与 STL 体系开篇接下来我们详细讲解一下非类型参数1介绍非类型模版参数非类型参数用一个常数作为类(函数)模版的参数在类函数模版中可以当成一个常量进行使用。本质上是一个编译期常量值。C17只支持整形int/short/size_t/bool 指针 auto不支持浮点数类类型字符串在C20之后才支持了浮点数类类型使用展示//#define N 10//这里使用宏定义N的值不能进行修改 //这里使用非类型模版参数N可以进行修改 template size_t N 10//可以使用缺省值 class arr { private: int arr[N]; int top; }; int main() { arr3 a1;//中的数字就是自定义的容量N arr6 a2; arr a3;//使用缺省值 return 0; }2array数组cplusplus文档array文档array是一个静态数组使用的是随机迭代器迭代器为原生指针。需要包含头文件#include array下面是使用的案例主要包括越界检查template class T,size_t N 20 class Myarray { public: T operator[](size_t index) { assert(index N);//底层检查越界 return arr[index]; } private: T arr[N]; size_t size; }; int main() { //array是一个静态数组使用的是随机迭代器原生指针 arrayint, 10 a1; //类似于下面的静态数组 int arr[10]; //这两者有一个核心的区别越界的检查 //上面的静态数组arr 在读时不进行检查 //cout arr[10] endl;//arr[10]在底层就是一个指针解引用 //在写时进行抽查就是检查arr数组的后两个位置的值是否被修改 //arr[10] 11; //arr[11] 12; //arr[20] 23; //array越界的读和写都会被检查出来 //原因是他调用的是函数,调用的函数类似于上面的Myarray //a1[11]; //a1[13] 13; vectorint v2(10, 100); //vector在堆上开辟,Debug下会在3个指针的基础上加上一个代理指针 //release下就是三个指针 cout sizeof(v2) endl;//Debug下32release下24 //array静态在栈上开辟大小需要看开辟了多少空间 cout sizeof(a1) endl;//40 //栈上管理内存比堆上容易 return 0; }静态数组arr下的抽查二模版特化⭐1函数模板特化我们先来看一组案例日期类内含日期的比较逻辑//日期类 class Date { friend ostream operator(ostream _cout, const Date d); 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); } private: int _year; int _month; int _day; };//函数模版 template class T bool LessFunc(const T a1, const T a2) { return a1 a2; } int main() { cout LessFunc(1, 2) endl;//结果正确1 Date d1(2015, 3, 4); Date d2(2018, 5, 13); cout LessFunc(d1, d2) endl;//结果正确1 Date* p1 d2; Date* p2 d1; //由于比较的是地址而地址是随机分配的导致结果错误 cout LessFunc(p1, p2) endl;//结果错误1 const Date* p3 d2; const Date* p4 d1; cout LessFunc(p3, p4) endl;//结果错误1 return 0; }上面的模版不能支持下面两种特殊情况这时候就需要进行特化//函数模版 template class T bool LessFunc(const T a1, const T a2) { return a1 a2; } //函数特化 //Date* 和 const Date*类型的参数用下面的 template bool LessFuncDate*(Date* const a1, Date* const a2) { return *a1 *a2; } template bool LessFuncconst Date*(const Date* const a1, const Date* const a2) { return *a1 *a2; }此时加上下面的两个特化后结果就是正确的由此引出模版特化的概念在原模版的基础上针对特殊类型进行特殊化的实现方式。模版特化不只有上面的函数特化一种类型还有类模版特化。函数模板特化的步骤1必须要有一个基础的模板才能进行特化2关键字template后面要加上3函数名后面要加上面特化的类型。4函数模板特化的参数必须要和基础模板的参数类型完全相同。2全特化和偏特化模板的特化分为两种全特化将模板列表中的参数全部确定化。偏特化只指定一部分参数进行特化或者对参数加上特定模式的限制例如限制为指针引用相同类型template class T1,class T2 class Data { public: Data() { cout DataT1,T2 endl; } private: T1 d1; T2 d2; }; //类模板-全特化 template class Dataint, double { public: Data() { cout Dataint,double endl; } }; //偏特化 templateclass T1 class DataT1,double { public: Data() { cout Dataclass T1,double endl; } }; int main() { Dataint, char d1; Dataint, double d2; Datadouble, double d3; return 0; }运行展示特化会优先走实例化少的例如Dataint,double就不会走Dataclass T1,double偏特化指针与引用templateclass T1,class T2 class DataT1*, T2* { public: Data() { cout DataT1*,T2* endl; } }; templateclass T1,class T2 class DataT1, T2 { public: Data() { cout DataT1T2 endl; } }; templateclass T1,class T2 class DataT1*, T2 { public: Data() { cout DataT1*,T2 endl; } }; int main() { //Dataint, char d1; //Dataint, double d2; //Datadouble, double d3; Datachar*, double* d4; Datachar, double d6; Datachar*, double d7; return 0; }运行结果特殊内容补充三模板分离编译1,介绍分离编译一个程序项目由若干个源文件共同实现每个源文件单独编译生成目标文件最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。一个C/C程序要运行下去一般要经历这几个过程详细内容可以看下面两期博客详解预处理编译与链接2模板的声明和定义为什么不能分离上面的Func是一个普通函数函数声明放在.h文件当中定义放在Func.cpp当中当在最后一步时将文件和并到一起时进行实例化可以直接找到函数的地址。而Add模板函数由于没有确定类型T导致不能进行实例化。没有进行实例化导致在链接时找不到函数的地址无法完成链接。解决方法1将函数模板的声明和定义放到同一个文件当中。2模板定义位置显式实例化不推荐四函数模板的优缺点⭐优点1,极高的代码复用性可以直接省去写类似逻辑的重载函数2. 编译期的类型安全相比于 C 语言中的宏#define MAX(a,b) ...或void*函数模板是类型安全的。宏只是简单的文本替换没有类型检查容易产生副作用。模板在编译期推导类型如果你传入不支持比较的类型编译器会直接报错而不是在运行时崩溃。3性能高函数模板是在编译期实例化的。编译器会像生成普通函数一样为你生成具体的机器码。编译器通常会将短小的模板函数内联inline进一步提升执行效率。泛型代码的性能通常与手写的具体类型代码完全一致缺点1代码膨胀编译器会为每一种使用到的类型生成一份独立的函数实例。2编译时间变长模板的解析推导和实例化都在编译期进行。3报错信息难懂当模板出现类型错误时编译器会打印出极其冗长的错误信息。4无法分离编译模板的声明和定义无法分离结尾本篇文章中所涉及到的代码gitee链接泛型编程-模板进阶以上就是本文的全部内容了如果对你有所帮助的话请大佬不妨给博主来个“一键三连”这对我是大大的支持也能让博主产出更优质的内容。往期回顾C 泛型世界的两块拼图容器适配器与仿函数总结本文详细讲解了C模板进阶内容涵盖非类型模板参数如size_t、array容器的越界检查、函数与类模板特化全特化、偏特化、模板分离编译问题及函数模板的优缺点。⚡把上面的内容吃透就休息一下吧⚡
返回列表