【C++】模板进阶 —— 非类型模板参数 | 特化 | 模板的分离编译
创始人
2024-01-20 21:18:13
0

🌈欢迎来到C++专栏~~模板进阶


  • (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
  • 目前状态:大三非科班啃C++中
  • 🌍博客主页:张小姐的猫~江湖背景
  • 快上车🚘,握好方向盘跟我有一起打天下嘞!
  • 送给自己的一句鸡汤🤔:
  • 🔥真正的大师永远怀着一颗学徒的心
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
  • 🎉🎉欢迎持续关注!
    在这里插入图片描述

请添加图片描述

模板进阶

  • 🌈欢迎来到C++专栏~~模板进阶
    • 一. 非类型模板参数
    • 二. 模板的特化
      • 🌈函数模板的特化
      • 🌈类模板的特化
        • 🎨全特化
        • 🎨偏特化
    • 三. 模板的分离编译
    • 四. 总结
  • 📢写在最后

请添加图片描述

一. 非类型模板参数

模板参数分类类型形参非类型形参
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。

比如我们要实现一个静态的数组

#define N 100;template
class array
{//....
private:T _a[N];
};

但是,这样无法灵活控制栈的大小 —— 不是泛型化

int main()
{array a1;   //只能存100array a2;  //存1000不可以return 0;
}

🧐这就要引入非类型模板参数

非类型模板参数,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用

template
class array
{//....
private:T _a[N];
};int main()
{array a0;   //可以给缺省参数:从右向左缺,且连续array a1;   //100array a2;  //1000return 0;
}

注意:

  1. 浮点数、类对象以及字符串不允许作为非类型模板参数的(只支持整形)
  2. 非类型的模板参数必须在编译期就能确认结果

🥑吐槽array——>不受欢迎

	array1 a0;   //C++11int a1[10];       //C//真正的区别:越界的检测//函数调用[]:assert检测有没有越界a0[10];//指针的解引用 --- 抽查是否越界,只针对越界写,越界读不检查a1[10] =10;

二. 模板的特化

🌈函数模板的特化

🌊函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
struct Date
{Date(int year, int month, int day):_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);}private:int _year;int _month;int _day;
};// 函数模板 -- 参数匹配
template
bool Greater(T left, T right) 
{return left > right;
}//特化--针对某些类型进行特殊化处理
template<>
bool Greater(Date* left, Date* right)
{return *left > *right;
}int main()
{cout << Greater(1, 2) << endl; // 可以比较,结果正确Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Greater(d1, d2) << endl; // 可以比较,结果正确Date* p1 = &d1;Date* p2 = &d2;cout << Greater(p1, p2) << endl; // 可以比较,结果错误return 0;
}

一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出,有点鸡肋的😂

🌈类模板的特化

特化不能单独存在,有鸡才有蛋

🎨全特化

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

在这里插入图片描述

如果数据类型T 是Date* 默认是按照地址比较的,那这样直接比较大小的结果不是我想要的,我想要按对象比较

	void test_priority_queue3(){//priority_queue pq; //默认比较地址大小priority_queue, ljj::lessPDate> pq;pq.push(new Date(2022, 10, 11));pq.push(new Date(2022, 11, 11));pq.push(new Date(2022, 12, 19));pq.push(new Date(2022, 4, 10));//默认比较地址大小,若想比较日期大小,自己写仿函数while (!pq.empty()){cout << *pq.top() << endl;pq.pop();}}

上篇博客我们知道了,可以借助模板的第三个参数Compare的口子,自己写一个仿函数来实现,现在还可以提供一个新方法:针对Date*特化

	templatestruct Greater{bool operator()(const T& x1, const T& x2) const{return x1 > x2;}};template<>struct Greater{bool operator()(Date*& x1,  Date*& x2) const{return *x1 > *x2;}};

注意不用仿函数了,对Date*类型进行了特殊化处理

	priority_queue pq;

🎨偏特化

偏特化,是对模板参数进一步进行条件限制
比如下面的模板类:

using namespace std;template
class Data
{
public:Data() { cout << "Data" << endl; }
private:T1 _d1;T2 _d2;
};

🥑 部分特化
将模板参数类表中的一部分参数特化。第一个随意,第二个特化指定值

// 将第二个参数特化为int
template 
class Data {
public:Data() { cout << "Data" << endl; }
private:T1 _d1;int _d2;
};int main()
{Data d1; //模板// 偏特化Data d3;Data d4;return 0;
}

🔥进一步限制,只要你是指针,不管你什么类型

//两个参数偏特化为指针类型
template 
class Data 
{
public:Data() { cout << "Data" << endl; }private:T1 _d1;T2 _d2;
};//两个参数偏特化为引用类型
template 
class Data 
{
public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout << "Data" << endl;}
private:const T1& _d1;const T2& _d2;
};int main()
{Data d5;Data d6;Data d7(1,2);return 0;
}

三. 模板的分离编译

复习地址传送门:编译链接
分离编译:一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式,此举可以帮助我们更好的维护项目,看.h了解框架设计功能,看.cpp了解实现细节

✅编译链接过程:

a.h  a.cpp  test.cpp

🔒预处理:宏替换、头文件展开、条件编译、去注释

a.i    test.i

🔒编译:C ➡️ 汇编

a.s    test.s

🔒汇编:汇编 ➡️ 可重定向二进制目标文件

a.o   test.o

🔒链接:将多个obj文件合并成一个,并处理没有解决的地址问题

普通函数分离编译没有问题,模板函数分离编译会出现链接不上错误,分析如下 ——

原因时:在链接之前,二者并不会交互,所以头文件只有模板,T无法确定所以没有实例化,insert等就没有进符号表,链接在符号表中找不到,自然就报错

在这里插入图片描述

➰解决方法

🔸1. 将声明和定义不要分离到.h和.cpp,放在同一个文件中(推荐)

那么,在编译阶段,test.i中,头文件展开后,直接就有模板的定义和实例化,可以直接填上调用地址,不需要链接时去找了

🔸2.显示实例化(不推荐)
缺点:用一个就得显示实例化一个,非常麻烦,泛型失去意义

#include"a.h"void F1(int N)
{// 2.编译阶段:生成汇编代码cout << "void F1(int N)" << endl;
}template
void F2(const T& N)
{// 2.编译阶段:不处理,没有实例化,无法生成汇编代码cout << "void F2(const T& N)" << endl;
}// 显式实例化
template
void F2(const int& N);

四. 总结

【优点】:

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

【缺陷】:

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

📢写在最后

广州什么时候好起来

在这里插入图片描述

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...