🌟🌟hello,各位读者大大们你们好呀🌟🌟
🚀🚀系列专栏:【C++的学习】
📝📝本篇内容:非类型模板参数;模板的特化;模板分离编译
⬆⬆⬆⬆上一篇:进程程序替换
💖💖作者简介:轩情吖,请多多指教(> •̀֊•́ ) ̖́-
1.非类型模板参数
模板参数分为类型形参和非类型形参
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量使用
注意:①浮点数、类对象以及字符串是不允许作为类型模板参数的
②非类型的模板参数必须在编译期就能确认结果
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
template<class T, size_t N>
int Add(const T& x,const T& y)
{
if (N > 0)
{
return x + y;
}
else
{
return 0;
}
}
int main()
{
cout<<Add<int,10>(1,2)<<endl;
return 0;
}
2.模板的特化
2.1概念
通常情况下,使用模板可以实现一些与类型无关的代码,但是对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理
在原模板类的基础上,针对特殊类型所进行特殊化的实现方式
模板特化中分为函数模板特化与类模板特化
2.2函数模板特化
步骤:
①必须要先有一个基础的函数模板
②关键字template后面接一对空的尖括号<>
③函数名后跟一对尖括号,尖括号中指定需要特化的类型
④函数形参表:必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
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;
};
template<class T>
bool Less(T x,T y)
{
return x < y;
}
int main()
{
Date d1(2023, 4, 11);
Date d2(2023, 4, 12);
cout << Less(1, 2) << endl;
cout<<Less(d1, d2)<<endl;
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl;
return 0;
}
可以发现在比较p1和p2时,会出现错误,原因就是因为它比较了指针,因此这时候就需要函数模板特化
当p1和p2调用Less函数时,会匹配到函数模板特化所对应的函数
2.3类模板特化
2.3.1全特化
全特化即是将模板参数列表中所有的参数都确定化
#include <iostream>
#include <stdbool.h>
using namespace std;
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;
};
template<class T>
struct Less
{
bool operator()(T x,T y)
{
return x < y;
}
};
template<>
struct Less<Date*>
{
bool operator()(Date* x, Date* y)
{
return *x < *y;
}
};
int main()
{
Date d1(2023, 4, 11);
Date d2(2023, 4, 12);
cout<<Less<int>()(1, 2)<< endl;
cout<<(Less<Date>()(d1, d2))<<endl;
Date* p1 = &d1;
Date* p2 = &d2;
cout << (Less<Date*>()(p1, p2)) << endl;
return 0;
}
可以看到这个类对象会调用特化的类模板
2.3.2偏特化
任何针对模板参数进一步进行条件限制设计的特化版本
偏特化有以下两种表现方式:
①部分特化:将模板参数类表中的一部分参数特化
#include <iostream>
using namespace std;
template<class T, class Y>
class Data
{
public:
Data()
{
cout << "<class T,class Y>" << endl;
}
};
template<class Y>
class Data<double,Y>
{
public:
Data()
{
cout << "<double,Y>" << endl;
}
};
int main()
{
Data<int,int>();
Data<int, double>();
Data<double,int>();
return 0;
}
可以看到,在上面的部分特化中,将一部分的参数进行了确定,在调用第三个对象时,编译器根据类型,使用了特化的类模板
②参数进一步的限制:偏特化并不仅仅是指特化部分参数,而是针对模板更进一步的条件限制所设计出来的一个特化版本
#include <iostream>
using namespace std;
template<class T, class Y>
class Data
{
public:
Data()
{
cout << "<class T,class Y>" << endl;
}
};
template<class T,class Y>
class Data<T*,Y*>
{
public:
Data()
{
cout << "<T*,Y*>" << endl;
}
};
int main()
{
Data<int,int>();
Data<int, double>();
Data<double*,int*>();
return 0;
}
进行了进一步的限制,接收的模板参数必须是指针
3.模板分离编译
3.1分析错误
//source.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include "head.h"
using namespace std;
int main()
{
Add(1);
return 0;
}
//head.h
template<class T>
void Add(T x);
//Add.cpp
template<class T>
void Add(T x)
{
}
可以试一下把模板的声明和定义,会出现链接错误,为什么呢?
这是因为,在source文件中包含了head.h这个头文件,也就是说在预处理阶段,head.h就被展开在source.cpp这个文件中了,由于声明过了,说明后面链接时能够找到对应的函数实现,所以说在编译阶段的语法检查中通过了。在汇编阶段会生成符号表,但是Add没有对应的地址,只能空着。而Add.cpp
这个源文件中由于是函数模板,并不知道具体类型,因此也不知道开多大空间的栈帧,导致并不会实例化。等到链接的时候,会合并符号表,但是Add对应的地址还是空的,因此出现了链接错误。
3.2解决方法
①将声明和定义放在同一个文件“xxx.hpp”里面或“xxx.h”其实也是可以的
②模板定义的位置显示实例化
🌸🌸模板进阶的知识大概就讲到这里啦,博主后续会继续更新更多C++的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪