【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()函数地址说明要调用该函数

相关推荐
TU^1 分钟前
C语言习题~day16
c语言·前端·算法
sukalot3 分钟前
windows C++-创建基于代理的应用程序(下)
c++
gopher95113 分钟前
final,finally,finalize的区别
java·开发语言·jvm
吃什么芹菜卷6 分钟前
深度学习:词嵌入embedding和Word2Vec
人工智能·算法·机器学习
m0_687399849 分钟前
QT combox 前缀匹配
开发语言·数据库·qt
wclass-zhengge10 分钟前
数据结构与算法篇(树 - 常见术语)
数据结构·算法
labuladuo52015 分钟前
AtCoder Beginner Contest 372 F题(dp)
c++·算法·动态规划
夜雨翦春韭17 分钟前
【代码随想录Day31】贪心算法Part05
java·数据结构·算法·leetcode·贪心算法
汤兰月17 分钟前
Python中的观察者模式:从基础到实战
开发语言·python·观察者模式