C++模板进阶

🌟🌟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++的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

轩情吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值