C++初阶之模板进阶

个人主页点我进入主页

专栏分类:C语言初阶 C语言进阶 数据结构初阶 Linux C++初阶算法

欢迎大家点赞,评论,收藏。

一起努力,一起奔赴大厂

目录

一.非类型模板参数

二.模板的特化

2.1引入

2.2全特化

2.3偏特化

三.模板的分离编译

3.1链接错误

3.2链接错误是如何产生的

3.3如何正确分离编译

3.4按需实例化


一.非类型模板参数

非类型模板参数就是模板的参数有一个常量,我们可以参考下面的代码:

template<class T, size_t N = 10>
class array
{
public :
	T& operator[](size_t index)
	{
		return _arr[index];
	}
	int size()const
	{
		return _size;
	}

private :
	T _arr[N];
	int _size=0;
};

在C++98中我们只支持整形的做为非类型模板参数,在后面的一些版中开始支持其他的类型作为非类型模板参数。在库中我们就有一个类似的设计array这个,我们需要知道这个非常的不好用,因为它实现的功能使用vector就可以实现,而且array在栈上开辟的空间 ,非常容易造成栈溢出,所以可以说这个基本没有任何用处。

二.模板的特化

2.1引入

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;
};

ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}

template<class T>
bool Less(T left, T right)
{
return left < right;
}
void test1()
{
	cout << Less(1, 2) << endl; // 可以比较,结果正确
	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl; // 可以比较,结果正确
	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; // 可以比较,结果错误

}

在日期类中我们比较当传入的是值的时候可以比较正确,但是当我i们传入指针时,我们就不能保证这个正确了,这个主要就是比较的是地址,而不是值,因此我们需要特化。

2.2全特化

template<class T>
bool Less(T left, T right)
{
	cout << "Less(T left, T right)" << endl;
	return left < right;
}
template <>
bool Less(Date* left, Date* right)
{
	cout << "Less(Date * left, Date * right)" << endl;
	return *left < *right;
}

我们需要在已经有的模板上再次写这个模板对它进行特化,但是template后面跟一个尖括号,尖括号里什么也不写,然后将类型写上,再写具体的代码。我们利用上面的代码运行可以看到:

在这里我们比较指针时会走我们的全特化,

2.3偏特化

偏特化和全特化类似,它只是特化模板的一部分,它就是对参数进一步进行限制,我们可以看下面的代码:

template <class T1,class T2>
class A
{
public :
	A()
	{
		cout<<"A< T1,T2>" << endl;
	}
private:
	T1 _al;
	T2 _a2;
};

template <class T1>
class A<T1,int>
{
public:
	A()
	{
		cout << "A< T1,int>" << endl;
	}
private:
	T1 _al;
	int _a2;
};

template <class T1,class T2>
class A<T1*, T2*>
{
public:
	A()
	{
		cout << "A< T1*, T2*>" << endl;
	}
private:
	T1* _al;
	T2* _a2;
};
void test1()
{
	A<int, char> a1;
	A<int, int>a2;
	A<int*, int*>a3;
}

运行结果为

在类的使用特化时我们需要在已有的类中再次定义这个类,但是类的后面需要加上括号,括号中的内容是类型,原来的类中的template中有几个参数它就有几个参数,特化的内容不同,没有限制的相同,特化的template中的尖括号为没有精准限制的(它不是一个模糊的,不明确的例如int等,但是参数是T*就需要将T写上)。

三.模板的分离编译

3.1链接错误

我们将一个类进行分离编译,代码如下:

//.h

namespace bit
{
	template<class T, size_t N = 10>
	class array
	{
	public:
		int size() const;
	private:
		T _array[N];
		int _size=0;
	};
}


//.c

namespace bit
{
	template<class T, size_t N >
	int array<T,N>::size() const 
	{
		return _size;
	}
}

我们实例化一次运行可以看到出现了链接错误

链接错误就是找不到地址,我们可以实验一下,只在.h文件中写一个func函数,调用的时候就会出现链接错误,代码如下:

//.h为
void func();
//.c直接调用

这是由于在编译时有这个声明就可以了,连接时才会进行合并call地址,call的地址就是func定义的地方,但是没有定义,所以出现链接错误。 我们将func函数的定义写好后我们看汇编代码

它会call一下地址。

3.2链接错误是如何产生的

在编译阶段开始编译器会把模板进行实例化,由于我们是模板的分离编译,所以我们会造成在.h文件中没有定义的地址,.c文件中有定义但是不知道实例化成什么

3.3如何正确分离编译

我们需要将声明和定义在同一个.h文件中,这样在编译阶段就会实例化,不需要在链接找地址,我们的代码如下:

	template<class T, size_t N = 10>
	class array
	{
	public:
		int size() const;
	private:
		T _array[N];
		int _size=0;
	};

	template<class T, size_t N >
	int array<T, N>::size() const
	{
		return _size;
	}

3.4按需实例化

我们看代码

	template<class T, size_t N = 10>
	class array
	{
	public:
		int size() const;
		void print()
		{
			cout << size(2);
		}
	private:
		T _array[N];
		int _size=0;
	};

	template<class T, size_t N >
	int array<T, N>::size() const
	{
		return _size;
	}

我们可以看到print函数中是错误的,但是我们不适用print这个函数,编译器就不会报错,一旦使用这个就会出错,我们看两个实验:

bit::array<int> a1;
cout<<a1.size();
	bit::array<int> a1;
	a1.print();

造成这个结果的就是因为编译器会进行按需实例化,当我们需要的时候才会进行实例化,随着版本的更新,编译器在很久以前如果里面少写一个分号也可以编译过去,只要我们不去调用。

相关推荐
孤寂大仙v7 分钟前
【C++】STL----list常见用法
开发语言·c++·list
咩咩大主教1 小时前
C++基于select和epoll的TCP服务器
linux·服务器·c语言·开发语言·c++·tcp/ip·io多路复用
Ylucius3 小时前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
是店小二呀3 小时前
【C++】C++ STL探索:Priority Queue与仿函数的深入解析
开发语言·c++·后端
ephemerals__3 小时前
【c++】动态内存管理
开发语言·c++
CVer儿3 小时前
条件编译代码记录
开发语言·c++
程序猿练习生4 小时前
C++速通LeetCode简单第18题-杨辉三角(全网唯一递归法)
c++·算法·leetcode
汉字萌萌哒4 小时前
【2022 CCF 非专业级别软件能力认证第一轮(CSP-J1)入门级 C++语言试题及解析】
数据结构·c++·算法
th新港4 小时前
CCF201909_1
数据结构·c++·算法·ccf
意如流水任东西4 小时前
[C++]类和对象(上)
开发语言·c++