当前位置:网站首页>[Generic Programming] Full Detailed Explanation of Templates

[Generic Programming] Full Detailed Explanation of Templates

2022-08-09 22:46:00 Blade Cc

Ⅰ. 泛型编程

1.引入

在c语言中,If we want to write multi-type,And it is a function out of the same function,We can only write a few,It would appear to be redundant,It also increases the amount of code for programmers,于是c++中就引入了函数重载泛型编程的概念,Greatly simplifies our work!

Function overloading alone cannot meet the needs of generic programming,比如说下面的代码:

void Swap(int& x, int& y) {
    
	int tmp = x;
	x = y;
	y = tmp;
}
void Swap(double& x, double& y) {
    
	double tmp = x;
	x = y;
	y = tmp;
}
void Swap(char& x, char& y) {
    
	char tmp = x;
	x = y;
	y = tmp;
}
 
int main()
{
    
	int a = 0, b = 1;
	double c = 1.1, d = 2.2;
	char e = 'a', f = 'b';
 
	Swap(a, b);
	Swap(c, d);
	Swap(e, f);
 
	return 0;
}

好像靠函数重载来调用不同类型的 Swap,只是表面上看起来 “通用” 了 ,

实际上问题还是没有解决,有新的类型,还是要添加对应的函数……

用函数重载解决的缺陷:

重载的函数仅仅是类型不同,代码的复用率很低,只要有新类型出现就需要增加对应的函数.

代码的可维护性比较低,一个出错可能导致所有重载均出错.

2.什么是泛型编程

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段.模板是泛型编程的基础.

其中,模板分为两类,一类是函数模板,A class is a class template.

Ⅱ. 函数模板

1.函数模板概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本.

2.函数模板格式

template<class T1, class T2, class T3, …, class Tn>
返回值类型 函数名(参数列表)
{}

template 是定义模板的关键字,后面跟的是尖括号 < >

class 是用来定义模板参数的关键字**(也可以用typename,但是STL中多用class)**

T1, T2, …, Tn 表示的是函数名,可以理解为模板的名字,名字你可以自己取.

template<typename T>         // 模板参数列表 ———— 参数类型
void Swap(T& x, T& y) {
        // 函数参数列表 ———— 参数对象
	T tmp = x;
	x = y;
	y = tmp;
}

注意事项:

函数模板不是一个函数,因为它不是具体要调用的某一个函数,而是一个模板.就像 “好学生”,主体是学生,“好” 是形容 “学生” 的;这里也一样,“函数模板” 是模板,So the function template expression means “函数的模板” .所以,我们一般不叫它模板函数,应当叫作函数模板.A template function is a function that is instantiated with a template.

“函数模板不是一个实在的函数,编译器不能为其生成可执行代码.After defining the function templateJust a description of the function frame function,当它具体执行时,将根据传递的实际参数决定其功能.” —— 《百度百科》

3.函数模板的原理

函数模板是一个蓝图,它本身并不是函数,It is the way that the compiler generates a function of a specific concrete type模具.所以其实模板就是将本来应该我们做的重复的事情交给了编译器.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ZgZAJwr-1659941480624)(../../img/image-20220808091345012.png)]

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用.比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此.

通俗易懂的说,Here are three different function parameter types,不是同一个函数,We are only responsible for the type parameter,And the work of generating these functions is done for us by the compiler!

4.函数模板的实例化

用不同类型的参数使用模板参数时,成为函数模板的实例化.

模板参数实例化分为:隐式实例化显式实例化 ,下面我们来分别讲解一下这两种实例化.

① 隐式实例化

定义: 让编译器根据实参,推演模板函数的实际类型.

以下面addfunction code as an example:

template<class T>
T Add(const T& x, const T& y) 
{
    
	return x + y;
}
 
int main()
{
    
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;
	cout << Add(a1, a2) << endl;
	cout << Add(d1, d2) << endl;
 
	return 0;
}
运行结果:
30
30.3

现在思考一个问题,如果出现 a1 + d2 这种情况呢?实例化能成功吗?

Add(a1, d2);

The final program reported an error,That is, the compiler cannotTto deduce which of the two types you want to use.

解决方式

1. 传参之前先进行强制类型转换

template<class T>
T Add(const T& x, const T& y) 
{
    
	return x + y;
}
 
int main()
{
    
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;
	cout << Add(a1, a2) << endl;
	cout << Add(d1, d2) << endl;
 
	cout << Add((double)a1, d2) << endl;  //将a1强转为double,或者将d2强转为int都行
 
	return 0;
}

2. 写两个参数,那么返回的参数类型就会起决定性作用

template<class T1, class T2>
T1 Add(const T1& x, const T2& y)   // 那么T1就是int,T2就是double
{
     
	return x + y;      // 范围小的会像范围大的提升,int会转为double
} // 最后表达式会是一个double,但是最后返回值又是T1,是int,又会转
 
int main(void)
{
    
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;
 
	cout << Add(a1, d2) << endl;   // int,double 
 
	return 0;
}

3. 我们还可以使用 “显式实例化” 来解决

Add<int>(a1, d2);     // 指定实例化成int
Add<double>(a1, d2)   // 指定实例化成double

② 显式实例化

定义: 在函数名后的 < > 里指定模板参数的实际类型.

简单来说,显式实例化就是在中间加一个尖括号 < > 去指定你要实例化的类型.

(在函数名和参数列表中间加尖括号)

函数名 <类型> (参数列表);

所以对于上面的问题,我们可以用该方法解决:

template<class T>
T Add(const T& x, const T& y) 
{
    
	return x + y;
}
 
int main()
{
    
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;
	cout << Add(a1, a2) << endl;
	cout << Add(d1, d2) << endl;
 
	cout << Add<int>(a1, d2) << endl;     // 指定T用int类型
	cout << Add<double>(a1, d2) << endl;  // 指定T用double类型
 
	return 0;
}
运行结果:
30
30.3
30
30.2

解读:

像第一个 Add<int>(a1, a2) ,a2 是 double,它就要转换成 int .第二个 Add<double>(a1, a2),a1 是 int,它就要转换成 double.

这种地方就是类型不匹配的情况,编译器会尝试进行隐式类型转换.像 double 和 int 这种相近的类型,是完全可以通过隐式类型转换的.

总结:

函数模板你可以让它自己去推,但是推的时候不能自相矛盾.

你也可以选择去显式实例化,去指定具体的类型.

5.模板参数的匹配原则

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
// 专门处理int的加法函数
int Add(int x, int y) {
    
	return x + y;
}
 
// 通用加法函数
template<class T>
T Add(const T& x, const T& y) {
    
	return x + y;
}
 
int main()
{
    
	Add(1, 2); // 与非模板函数匹配,编译器不需要特化
 	Add<int>(1, 2); // 调用编译器特化的Add版本
 
	return 0;
}
  1. 对于非模板函数和同名函数模板,如果其他条件都相同,在调用时会优先调用非模板函数,而不会从该模板生成一个实例.如果模板可以产生一个具有更好匹配的函数,那么将选择模板.
// 专门处理int的加法函数
int Add(int left, int right) 
{
    
	return left + right; 
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right) 
{
    
	return left + right; 
}
void Test()
{
    
	Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}
  1. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

总结:

  • 函数调用时候,如果有现成的函数,绝对不去实例化模板,Because this will also lead to repetition.
  • 调用时候,If several parameters match,则优先匹配类型最合适的.

Ⅲ. 类模板

1. 类模板的定义格式

template <class T1, class T2, ..., class Tn>
class 类模板名 
{
    
    //类内成员定义
}

有了类模板,We have a unique advantage:不用再去 typedef 类型

我们之前cFor some data structures in the language such as stack,我们在设置int类型的时候,只能去 typedef 为 int,然后如果需要 double then go again typedef 为 double,非常麻烦,And this method can't apply for one at the same time int类型 和 double类型 的 stack.

Now with class templates,We just need to set its format when defining the declaration!

// 注意:Stack不是具体的类,is generated by the compiler based on the type being instantiatedspecific type of mold
template<class T>
class Stack 
{
    
	//类内成员定义
};
 
int main()
{
    
	Stack<int> st1;   // 存储int
	Stack<double> st2;   // 存储double
 
	return 0;
}

注意事项

这里的 Stack 不是具体的类,is generated by the compiler based on the type being instantiatedspecific type of mold

2.类模板的实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟 <>,然后将实例化的类型放在 <> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类.

类名<类型> 变量名;

//如:
Stack<int> st1;
Stack<double> st2;

注意事项

这里的 Stack类名,Stack<int> 才是类型

3.类外定义类模板参数

If we define the function like this when we define the function outside the class, it will report an error:

template<class T>
class Stack {
    
public:
	Stack(T capacity = 4) 
		: _top(0) 
		, _capacity(capacity) {
    
		_arr = new T[capacity];
	}
	// 这里我们让析构函数放在类外定义
	void Push(const T& x);
	~Stack();
private:
	T* _arr;
	int _top;
	int _capacity;
};
 
/* 类外 */
Stack::~Stack() {
          // 即使是指定类域也不行 
    ...
}

解答:

① Stack 是类名,Stack<int> 才是类型.这里要拿 Stack<T> 去指定类域才对.

② 类模板中的函数在类外定义,没加 “模板参数列表” ,编译器不认识这个 T .类模板中函数放在类外进行定义时,需要加模板参数列表.

** 代码演示:**我们现在来看一下如何添加模板参数列表!

template<class T>
class Stack {
    
public:
	Stack(T capacity = 4) 
		: _top(0) 
		, _capacity(capacity) {
    
		_arr = new T[capacity];
	}
 
	// 这里我们让析构函数放在类外定义
	~Stack();
private:
	T* _arr;
	int _top;
	int _capacity;
};
 
// 类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
Stack<T>::~Stack() {
       // Stack是类名,不是类型! Stack<T> 才是类型,
	delete[] _arr;
	_arr = nullptr;
	_capacity = _top = 0;
}

知识点小结:

① Templates can have 非类型参数,用于指定大小,可以根据指定的大小创建动态结构(这个下面会讲到).

② 模板 运行时 不检查数据类型,data security is not guaranteed,相当于类型的宏替换.

类模板 是一个类家族,而 模板类 是通过 The concrete class instantiated by the class template.

④ 类模板中的 成员函数 全是模板函数,在类外定义时都Must be defined via full template syntax.

Ⅳ. 非类型模板参数(Nontype Template Parameters)

1.问题的引入

举例: Suppose we want to define a static array:

#define X 1000

template<class T>
class Srray
{
    
	T _arr[X];
};

int main()
{
    
	Srray<int> a1;	
	Srray<int> a2;  
    
    return 0;
}

这个时候我们发现,The size of this static array is fixed,we can't let define Change the value at runtime,如果我想让 a1 开1000个空间,让 a2 开100个,那对于 a2 来说,是不是就浪费了900个空间了…What should we do then??

At this time, this non-type template parameter is introduced!

2.What is a non-type template parameter?

注意看,our common definition T 是类型,而 N here is not the type,而是一个常量

The type template parameter defines a virtual type,What matters is what you want to pass,而 A non-type template parameter defines a constant.

               "非类型模板参数"
                     
template<class T, size_t N> class array;
                 
        "类型模板参数"

模板参数分为 类型形参 与 非类型形参.

类型形参:出现在模板参数列表中,跟在 class 或者 typename 之类的参数类型名称.

非类型形参:就是用一个常量作为类 (函数) 模板的一个参数,在类 (函数) 模板中可将该参数当成常量来使用.

Since we have this non-type template parameter,we try to solve the problem!

//#define X 1000

template<class T, size_t X>
class Srray
{
    
	T _arr[X];
};

int main()
{
    
	Srray<int, 100> a1;	 //开辟了100a large static array
	Srray<int, 1000> a2;  //开辟了1000a large static array

	return 0;
}

Here we define a constant in the template X,let it do the size of the array.

So we can instantiate Srray When specifying the size of its instantiated object,分别传 100 和 1000.

注意事项:

① non-type template parameter is yes 整形常量,是不能修改的.

template<class T, size_t X>
class Srray
{
    
public:
	void Change()
	{
    
		X = 10;
	}
private:
	T _arr[X];
};

int main()
{
    
	Srray<int, 100> a1;	 //开辟了100a large static array
	Srray<int, 1000> a2;  //开辟了1000a large static array

	a1.Change();//编译器会报错,因为X是常量,不能修改

	return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0IpNcga7-1659941480625)(../../img/image-20220808111816645.png)]

② Some types cannot be used as non-type template parameters,比如浮点数、class objects and strings.
(也就是说,只能用 size_t 、int 、char)(char 也算整型,is just a one-byte integer)

template<string S>
class A
{
    
	//类内成员定义
};

template<double D>
class B
{
    
	//类内成员定义
};

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o7RWxGZE-1659941480625)(../../img/image-20220808112250609.png)]

③ 非类型的模板参数必须在编译期就能确认结果

3.STL中的Array(不重要)

官方文档:array - C++ Reference

Now learning about non-type template parameters,Let's look back now array:

arrayC++11 新增的,is there anything unique about it?很可惜,基本没有,并且 vector Can be completely crushed array……

#include <iostream>
#include <array>
#include <vector>
using namespace std;
 
int main()
{
    
    vector<int> v1(100, 0);
    array<int, 100> a1;
    
    cout << "size of v1: " << sizeof(v1) << endl;
    cout << "size of a1: " << sizeof(a1) << endl;
 
    return 0;
}
 运行结果:
size of v1: 32
size of a1: 400

vector It is on a heap with a large space and array It was opened on the stack of an inch of land and an inch of gold.,The heap is much bigger than the stack.

最尴尬的是 array can do almost vector 都能做,因为 vector 的存在 array seems a little useless.

Compared with the original array,array The biggest advantage is also just having an out-of-bounds check,Both read and write can check for out-of-bounds.

If you want to compare, you can only bully the native array,However, in the face of the powerful vector,array Completely incapable of parrying.

总结:array compared to native arrays,有Advantages of out-of-bounds checking,实际中建议直接用 vector.

Ⅴ.模板的特化

1.引入:Prepare special templates for special types

有时候,Compile default function templates and class templates(在一些特殊场景下)Can't handle the need logic correctly,At this time, you need to specialize for these specified types,So we need to do template specialization.

举例①: 字符串类型的比较

template<class T>
bool IsSame(const T& a, const T& b)
{
    
	return a == b;
}

int main()
{
    
	//For general type comparison,没有问题
	cout << IsSame(1, 2) << endl;
	cout << IsSame(1.1, 2.2) << endl;

    //There seems to be out of the question!
	char s1[] = "liren";
	char s2[] = "liren";
	cout << IsSame(s1, s2) << endl;

	const char* s3 = "lirendada";
	const char* s4 = "lirendada";
	cout << IsSame(s3, s4) << endl;

	return 0;
}

解答:

But it's not right,Strings we compare are character size,而不是比较地址,所以出现了这种情况,Templates can't help us either,So we have to do our own specialization!

举例②: 自定义类型的比较

#include "Date.h" /* Introduce your own implementation of the date class */
 
/* Determine if the left number is less than the right number */
template<class T>
bool Less(T left, T right) {
    
    return left < right;
}
 
int main()
{
    
    cout << Less(1, 2) << endl;        // 可以比较,结果正确
 
    Date d1(2022, 7, 7);
    Date d2(2022, 7, 8);
    cout << Less(d1, d2) << endl;      // 可以比较,结果正确
 
    Date* p1 = new Date(2022, 7, 16);
    Date* p2 = new Date(2022, 7, 15);
    cout << Less(p1, p2) << endl;      // 可以比较,结果正确
 
    return 0;
}

运行结果:(We run a few discoveries,the result is unstable,对于 Date* Sometimes true, sometimes false)

上述示例中,p1指向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内容,而比较的是p1和p2指针的地址,这就无法达到预期而错误.

此时,就需要对模板进行特化.即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式.模板特化中分为函数模板特化类模板特化.

2.函数模板的特化

There are two ways to specialize a function template:

方法一:

  1. 首先,必须要先有一个基础的函数模板.
  2. 其次,关键字 template followed by an empty pair of angle brackets <> .
  3. 然后,函数名后跟一对尖括号,Angle brackets specify what needs to be specialized.
  4. 最后,函数形参表必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误.

方法二:

​ Write the required functions and types directly,That is, write the overloaded function directly(比较简单一点).

例子①: 对于字符串

#include<cstring>

template<class T>
bool IsSame(const T& a, const T& b)
{
    
	return a == b;
}

//方法一:standard specialization
template<>
bool IsSame<const char*>(const char* const& a, const char* const& b)
{
    
	return strcmp(a, b) == 0;
}

//方法二:Specialized character array type
bool IsSame(char* a, char* b)
{
    
	return strcmp(a, b) == 0;
}

//方法二:Specialization of String Constant Types
bool IsSame(const char* a, const char* b)
{
    
	return strcmp(a, b) == 0;
}

int main()
{
    
	//For general type comparison,没有问题
	cout << IsSame(1, 2) << endl;
	cout << IsSame(1.1, 2.2) << endl;

	char s1[] = "liren";
	char s2[] = "liren";
	cout << IsSame(s1, s2) << endl;

	const char* s3 = "lirendada";
	const char* s4 = "lirendada";
	cout << IsSame(s3, s4) << endl;

	return 0;
}
 运行结果:
0
0
1
1

问题: Why the parameter type in the standard specialization is const char const& a*,而不是 const char &* ?

解答:

​ Because the original template parameter inside is const T& a,而这里的 const 是用来修饰 a 的,After specialization, we just put T 特化为 const char*,But the original type is const & 的,According to the above specialization requirements fourth point,函数形参表必须要和模板函数的基础参数类型完全相同,So the specialized parameter type must be the const & 带上,最后就变成 const char* const& a .

例子②: 对于自定义类型

#include <iostream>
#include "Date.h"
using namespace std;
 
// Essential original
template<class T>
bool Less(T left, T right) 
{
    
    return left < right;
}
 
// 针对某些类型要特殊化处理 ———— Use template specialization to solve
template<>
bool Less<Date*>(Date* left, Date* right) 
{
    
    return *left < *right;
}

// Ordinary functions for direct matching
bool Less(Date* left, Date* right) {
    
    return *left < *right;
}
 
int main()
{
    
    cout << Less(1, 2) << endl;       // 可以比较,结果正确
 
    Date d1(2022, 7, 7);	//Variables are opened in the stack area
    Date d2(2022, 7, 8);
    cout << Less(d1, d2) << endl;     
 
    Date* p1 = new Date(2022, 7, 16); //动态开辟,在堆区
    Date* p2 = new Date(2022, 7, 15);
    cout << Less(p1, p2) << endl;     
 
    return 0;
}
 运行结果:
1
1
0

解读: 对于普通类型,It will still adjust the normal template.对于 Date* The compiler will find that there is a special Date* and prepared specialization,The compiler will prefer this specialization.

思考: Now we add a normal function,Date* which version will go?

// 原模板
template<class T>
bool Less(T left, T right) {
    return left < right;
}
 
// template-specific
template<>
bool Less<Date*>(Date* left, Date* right) {
    return *left < *right;
}
 
// Ordinary functions for direct matching
bool Less(Date* left, Date* right) {
    return *left < *right;
}

答案: 函数重载,Will take the direct matching version of the normal function,Because it is ready,不用实例化.

结论:

函数模板specialization,Because it can be handled in the parameters,It's also easier to understand writing a normal function that matches the arguments.该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化,Just give the ordinary function of the matching parameter directly.

3.类模板的特化

Just now the function template does not have to be specialized,Because you can write a concrete implementation of the function.But class templates we can't implement a concrete actual type,就must be specialized了.

假设针对 T1 和 T2 是 int 和 int,want to specialize for this particular type,该怎么办?:

#include <iostream>
#include "Date.h"
using namespace std;
 
template<class T1, class T2>
class Data 
{
    
public:
	fun() 
    {
    
		cout << "Data<T1, T2>" << endl;
	}
private:
	T1 _d1;
	T2 _d2;
};
 
int main()
{
    
	Data<int, int> d1; //want to specialize it
	Data<int, double> d2; 
 
	return 0;
}

So to add a pair int int specialization of:

template<>
class Data<int, int>
{
    
public:
    fun()
    {
    
        cout << "Data<int, int>" << endl;
    }
}

With this specialization,我们的 d1 Will go to our specialized version of the complete definition,而 d2 Still go to our original template to complete the definition.

运行结果:

There are two types of specializations for class templates.:全特化偏特化(also called semi-specialization)

① 全特化

全特化:full specialization is将模板参数列表中所有的参数都确定化.我们上面写的 Data The specialization is the fully specialized version:

template<>
class Data<int, int>  //All parameters in the parameter list are deterministic
{
    
public:
    fun()
    {
    
        cout << "Data<int, int>" << endl;
    }
}

② 偏特化(半特化)

半特化: any template parameterSpecialized version that further carries out conditional design.Put some parameters in the class tableSome parameter specializations.(Half-specialization is not half-specialization,like half default is not half default)

For example, for the following template class:

template<class T1, class T2>
class Data
{
    
public:
	Data() {
    cout<<"Data<T1, T2>" <<endl;}
private:
	T1 _d1;
	T2 _d2;
};

偏特化有以下两种表现方式:

  • 部分特化:将模板参数类表中的Some parameter specializations.
// 将第二个参数特化为int
template <class T1>
class Data<T1, int> 
{
    
public:
	Data() 
    {
    
        cout<<"Data<T1, int>" <<endl;
    }
};
  • 参数更进一步的限制:偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本.
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
     
public:
	Data() 
    {
    
        cout<<"Data<T1*, T2*>" <<endl;
    }
};

//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
    
public:
     Data()
     {
    
     	cout<<"Data<T1&, T2&>" <<endl;
     }
};

void test2 () 
{
    
     Data<double , int> d1; // 调用特化的int版本
     Data<int , double> d2; // 调用基础的模板 
     Data<int *, int*> d3; // 调用特化的指针版本
     Data<int&, int&> d4(1, 2); // 调用特化的指针版本
}
 运行结果:
Data<T1, int>
Data<T1, T2>
Data<T1*, T2*>
Data<T1&, T2&>

③ Class template specialization application example

有如下专门用来按照小于比较的类模板Less:

#include<vector>
#include <algorithm>
template<class T>
struct Less
{
    
     bool operator()(const T& x, const T& y) const
     {
    
     	return x < y;
     }
};

int main()
{
    
	Date d1(2022, 7, 7);
    Date d2(2022, 7, 6);
    Date d3(2022, 7, 8);
    vector<Date> v1;
    v1.push_back(d1);
    v1.push_back(d2);
    v1.push_back(d3);
    
    // 可以直接排序,结果是日期升序
    sort(v1.begin(), v1.end(), Less<Date>());
    
    vector<Date*> v2;
    v2.push_back(&d1);
    v2.push_back(&d2);
    v2.push_back(&d3);

    // 可以直接排序,结果错误,Dates are not in ascending order,而v2中放的地址是升序
    // 此处需要在排序过程中,让sort比较v2中存放地址指向的日期对象
    // 但是走Less模板,sort在排序时实际比较的是v2中指针的地址,因此无法达到预期
    sort(v2.begin(), v2.end(), Less<Date*>());
 	return 0; 
}

通过观察上述程序的结果发现,对于日期对象可以直接排序,并且结果是正确的.但是如果待排序元素是指针,结果就不一定正确.因为:sort 最终按照 Less 模板中方式比较,所以只会比较指针,而不是比较指针指向空间中内容,此时可以使用类版本特化来处理上述问题:

// 对Less类模板按照指针方式特化
template<>
struct Less<Date*>
{
    
     bool operator()(Date* x, Date* y) const
     {
    
     	return *x < *y;
     }
};

特化之后,在运行上述代码,就可以得到正确的结果!

Ⅵ. 模板的优缺点

【优点】

  1. 模板复用了代码,节省资源,更快的迭代开发,C++ 的标准模板库(STL)因此而产生

  2. 增强了代码的灵活性

【缺陷】

  1. 模板会导致代码膨胀问题,也会导致编译时间变长

  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误(A generic error is the first message in the error list)

Ⅶ. 模板分离编译

假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:

// Add.h
template<class T> 
T Add(const T& left, const T& right);

// Add.cpp
#include "Add.h"
template<class T> 
T Add(const T& left, const T& right) 
{
    
	return left + right; 
}

// main.cpp
#include "Add.h"
int main()
{
    
	Add(1, 2);
	Add(1.0, 2.0);
    
 	return 0; 
}

在这里插入图片描述

简单来说,If the template is compiled separately,定义的地方没有实例化,Where instantiated there is no definition, only declaration.

解决方法:

  1. 将声明和定义放到一个文件“xxx.hpp”里面或者xxx.h其实也是可以的.推荐使用这种.

  2. 模板定义的位置显式实例化.这种方法不实用,不推荐使用.

【分离编译扩展阅读】 http://blog.csdn.net/pongba/article/details/19130

原网站

版权声明
本文为[Blade Cc]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/221/202208091933089103.html