目录

C模版进阶全知道

【C++】模版进阶全知道

https://i-blog.csdnimg.cn/direct/7cce7f2c85f84a90830b242b1736f4ef.jpeg

🍃 本系列为初阶C++的内容,如果感兴趣,欢迎订阅🚩

小编会用最简单最生动的比方,让大家学懂

🎊个人主页:[小编的个人主页])

 🎀   🎉欢迎大家点赞👍收藏⭐文章

✌️ 🤞 🤟 🤘 🤙 👈 👉 👆 🖕 👇 ☝️ 👍



我们知道,函数模版是 泛型编程 的一种思想, 通过函数模版,我们无需再重复实现一些逻辑相似的代码,而是通过函数模版进行复用,通过实例化出不同的类,达到对应的目的。而有些场景对于一些复杂的类型,比如指针,引用等,贸然使用函数模版,可能达不到我们想要的效果,就比如,函数模版就像一个通用的 “工具箱” ,里面有各种工具(通用模板)。但是当你需要处理一个特殊的任务(比如打印字符串时加上引号),你专门准备了一个 定制化 的工具, 这个工具只针对这个特殊任务,效果更好 。这就是我们将学习的类特化。

我们先来看这样一个例子,比如实现一个泛型的比较函数,然后我们来比较内置类型, 日期类,指针等

template<class T>
bool cmp(T left, T right)
{
	return left < right;
}

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
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);
	}

	//private:
	int _year;
	int _month;
	int _day;
};

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

int main()
{
	cout << cmp(1, 2) << endl;//正确
	Date d1(2025,3,5);
	Date d2(2025,3,1);
	cout << cmp(d1, d2) << endl;//正确
	Date* d3 = new Date(2025, 3, 5);
	Date* d4 = new Date(2025, 3, 1);
	cout << cmp(d3, d4) << endl;//错误,不确定
	return 0;
}

我们发现,比较一些内置类型以及自定义类型(只要自定义类型重载了自已的比较逻辑)好像都没有什么问题,但是如果参数是Date的指针,这样再套用com比较就有问题了,因此 指针在内存中分配的地址是不确定的 ,每次编译时,分配的地址是随机的。我们打印出来的结果也总是不确定的,因此,我们需要专门写一个 **Date的比较逻辑基于cmp** ,这就是函数的特化。 这个函数专门用于比较日期类的指针!!!,为他量身定制的。

//cmp的函数模版特化
template<>
bool cmp<Date*>(Date* left, Date* right)
{
	return *left < *right;
}

特化的格式,template<>里面什么都没有,特化的标志,

函数名

后面跟上你要特化的类型。

但是我们 不常用函数模版的特化

,因为我们完全可以再重载一个比较逻辑,不需要特化:

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

类模版特化又分别, 全特化和偏特化

全特化简单理解就是 所有参数都确定化 ,已经确定了:

比如:


template<class T1,class T2>
class car
{
public:
	car() { cout << "car<T1, T2>" << endl; }

private:
	T1 _d1;
	T2 _d2;
};

//全特化
template<>
class car<string,int>
{
public:
		car() { cout << "car<string,int>" << endl; }

private:
	string _d1;
	int _d2;
};

两个模版参数特化时都确定了。( 相当于给一个人定制化的完完全全

偏特化是 模版参数一部分特化或者是针对模版参数进一步限制 :

下面的例子:

//偏特化
template<class T1>
//部分参数特化
class car<T1, int>
{
public:
	car() { cout << "car<T1, int>" << endl; }

private:
	T1 _d1;
	int _d2;
};

//参数进一步限制
template<class T1, class T2>
class car<T1*, T2*>
{
public:
	car() { cout << "car<T1*, T2*>" << endl; }

private:
	T1* _d1;
	T2* _d2;
};


template<class T1, class T2>
class car<T1&, T2&>
{
public:
	car() { cout << "car<T1&, T2&>" << endl; }

private:
	const T1& _d1 =T1();
	const T2& _d2 =T2();
};

int main()
{
	car<int, double > c0;
	car<int, int> c1;
	car<string, int> c2;
	car<int*, int*> c3;
	car<int&, int&> c4;

	return 0;
}

输出结果:

https://i-blog.csdnimg.cn/direct/3cb034efdddd4cd78855fc5cadc38793.png

由此知道,编译器在类模版参数特化时,有现成吃现成(即有对应的特化),没有现成编译器才需要实例化相等于给一个人部分化定制

我们知道,一个源文件生成可执行程序分别要经过 预处理,编译,汇编,链接 四个大阶段。

一个程序可能由多个源文件构成,而每个源文件 单独编译形成目标文件 ,链接时将 所有源文件链接起来共同形成一个可执行程序

分离编译是指将程序的不同部分编译成独立的对象文件(.o 或 .obj),然后在链接阶段将这些对象文件组合成最终的可执行文件。分离编译要求每个编译单元(.cpp 文件)在编译时是 独立 的,编译器无法看到其他编译单元的内容。

也就是编译形成obj文件,就是 自已玩自已的 (都是独立的),等到链接时,把大家都链接起来。

先说结论,模版的声明和定义分开,即在头文件中声明,在源文件中定义,这会引发 链接错误

原因:汇编完后的多个obj目标文件, 由于函数模版无法实例化,因此无法放入到函数表中 ,链接时由于 寻址错误 ,就会链接失败。

即模板的实例化需要编译器在编译时知道模板的具体实现细节。如果模板的定义和实例化分布在不同的编译单元中,编译器在编译其中一个单元时可能无法看到模板的完整定义,从而无法生成正确的代码。

比如,我在Myfile.h头文件中声明函数模版,在file中定义。

“Myfile.h”

#include<iostream>
using namespace std;

template<class T>
void func2(const T& x);

“file1.cpp”

template<class T>
void func2(const T& x)
{
	cout << x << endl;
}

这时候会报出链接错误。

https://i-blog.csdnimg.cn/direct/f7deebf90ef74d68808ada420c1580b7.png

因此, 模版是不支持声明和定义分离的

解决办法:

1.将声明和定义放在一起

“Myfile.h”


template<class T>
void func2(const T& x)
{
	cout << x << endl;
}

方法2:在定义时显示实例化


template<class T>
void func2(const T& x)
{
	cout << x << endl;
}

template  void func2<int>(const int& x);//显示实例化

这样,编译器在链接时就不会根据无法实例化而报出链接错误

感谢你耐心地阅读到这里,你的支持是我不断前行的最大动力。如果你觉得这篇文章对你有所启发,哪怕只是一点点,那就请不吝点赞👍,收藏⭐️,关注🚩吧!你的每一个点赞都是对我最大的鼓励,每一次收藏都是对我努力的认可,每一次关注都是对我持续创作的鞭策。希望我的文字能为你带来更多的价值,也希望我们能在这个充满知识与灵感的旅程中,共同成长,一起进步。再次感谢你的陪伴,期待与你在未来的文章中再次相遇!⛅️🌈 ☀️