【c++】模板进阶

1. 非类型模板参数

定义一个数组类

c 复制代码
#include<iostream>
using namespace std;
#define N   1000
namespace zjw
{   
	// 定义一个模板类型的静态数组
	template<class T>
	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=N;
	};
}

我们需要两个对象,分别数组大小为10,和1000的话,只用上面一个类是不够的,两个类给不同的N值.

这里我们可以用非类型模板参数来解决这个问题,只需要一个类。

在这之前有一个知识点

c 复制代码
#include<iostream>

using namespace std;
#include<iostream>
using namespace std;
#define N   1000
namespace zjw
{
	// 定义一个模板类型的静态数组
	template<class T>
	class array
	{
	public:
		T& operator[](size_t index) { return _array[index]; }
		const T& operator[](size_t index)const { 
			size(1);//这里故意写错
			return _array[index]; }

		size_t size()const { return _size; }
		bool empty()const { return 0 == _size; }

	private:
		T _array[N];
		size_t _size = N;
	};
}

int main()
{

	zjw::array<int>st1;
	cout << st1.size() << endl;


}

size(1);//这里故意写错,但是在实例化st1,它只对要用的函数实例化,并不会检查其他类成员函数内部有没有错误。在operator\[\]中,size(1);//这里故意写错,就不会报错。而编译器只会对大体的结构进行检查,比如说哪个类成员函数少一个分号。

模板参数分类类型形参与非类型形参。
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

c 复制代码
#include<iostream>
using namespace std;

namespace zjw
{
	// 定义一个模板类型的静态数组
	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=N;
	};
}

浮点数、类对象以及字符串是不允许作为非类型模板参数的。c++20可以
非类型的模板参数必须在编译期就能确认结果。

c 复制代码
#include<iostream>
using namespace std;

namespace zjw
{
	
	template<string str>
     class A
   { 

    };

}
int main()
{
	zjw::A<"11111">st3;

}

2. 类模板的特化

上篇文章我们使用仿函数来给日期类比较大小,现在我们可以使用函数模板来给日期类比较大小

先定义一个日期类

c 复制代码
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;
};

然后实现函数模板比较大小

c 复制代码
 函数模板
template<class T>
bool Less(T left, T right)
{
	

	return left < right;
}

主函数

c 复制代码
int main()
{

	Date d1(2024, 2, 10);
	Date d2(2024, 3, 10);
	cout << Less(d1, d2) << endl;



}

此时d1<d2返回1.

实例化过程如下:


如果我们要给日期类指针进行比较大小

这里我们可以用特化

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
c 复制代码
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;
};
template<class T>
bool Less(T left, T right)
{
return left < right;
}
// 特化,针对某些特殊类型可以进行特殊处理
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}
int main()
{
	Date d1(2024, 2, 10);
	Date d2(2024, 3, 10);
	Date* p1 = new Date(2022, 7, 7);
	Date* p2 = new Date(2022, 7, 8);
   cout << Less(p1,p2) << endl;
}

或者这样类似函数重载,匹配最符合的,有现成的使用现成的

c 复制代码
template<class T>
bool Less(T left, T right)
{
return left < right;
}

bool Less(Date* left, Date* right)
{
	return *left < *right;
}

特化,针对某些特殊类型可以进行特殊处理

类模板的特化

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

c 复制代码
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; }
};
int main()
{
	
		
		Data<int, char> d2;
		

}

半特化->部分特化

将模板参数类表中的一部分参数特化。


c 复制代码
template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
// 半特化/偏特化
template<class T1>
class Data<T1, char>
{
public:
	Data() { cout << "Data<T1, char>" << endl; }
};

半特化->参数更进一步的限制

偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版

本。

c 复制代码
template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
// 半特化/偏特化,不一定是特化部分参数,可能是对参数的进一步限制
template<class T1, class T2>
class Data<T1*, T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }
};
c 复制代码
template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
template<class T1, class T2>
class Data<T1&, T2*>
{
public:
	Data() { cout << "Data<T1&, T2*>" << endl; }
};

5. 模板的分离编译

首先声明定义不分离:不会出现问题

声明定义分离:



链接出现问题,原因是在源.cpp中调用st1.size()会call这个函数的地址,在编译时候,去array.h中发现他只有声明,编译器会以为函数地址会在其他cpp中,等到链接的时候在call size函数地址,但是等到链接时,在cpp之间是分离的,,所以源.cpp中st1实例化的参数<int,10>不会给array.cpp中<T,N>;模板没有实例化,所以array.cpp中的size()函数没有地址,出现了链接错误。

解决方法:显示实例化

反汇编观察size()函数地址,call size()函数地址说明要调用该函数

相关推荐
starzy19905 分钟前
深入理解Scala隐式值:编译期自动装配
开发语言·后端·scala
布莱克60523 分钟前
Swift 条件语句详解:从 if 到 switch 的完整指南
开发语言·ssh·swift·switch·条件语句·guard·if
汉克老师36 分钟前
《从C++小白到算法高手:GESP 1—8级成长地图》第一讲:认识GESP——从编程新手到算法探索者
c++·gesp·信息学竞赛·小学生学c++
吴可可12339 分钟前
ActiveX调用WScript.Shell原理揭秘
开发语言
晴天的雨.9922 小时前
【C++】继承篇
java·开发语言·c++
SomeOtherTime2 小时前
第 1 讲:内存布局与所有权解剖——栈帧、胖指针与“移动/拷贝”在汇编层面的真实面目
java·开发语言·汇编
青少儿编程课堂2 小时前
蓝桥杯青少组图形化 + 全国青少年信息素养大赛 + 电子学会图形化考级:校园大富翁经营闯关解析
c++·python·算法·bfs·信息学竞赛
another heaven2 小时前
【C/C++ 第一次跨越(上):用 C++11/14 停止制造新的旧代码】
c语言·c++·制造
qq_406981032 小时前
c++ lamda(三)Lambda表达式
java·前端·c++
总要学点什么2 小时前
第106篇:三维特效-流动线效果——让一颗光点沿整条线一路滚到头
开发语言·javascript·ecmascript·gis·webgl·材质·cesium