C++: 模板(进阶)

学习目标

1.了解非类型模板参数

2.了解类模板的特化

3.知道模板分离编译会出现的问题

1.非类型模板参数(整型常量)

模板参数:

1.类型形参:在模板参数列表中,class/typename后的参数名称

2.非类型形参:整型常量

示例:

	template
	class arr
	{
	public:
		//......
	private:
		T _arr[N];//根据传递过来的N,来决定开多大的数组
		size_t _size;
	};

	void test1()
	{
		arr arr1;
	}

2.模板的特化

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板

// 函数模板 -- 参数匹配
template
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 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; // 可以比较,结果错误(比较的是指针)
	return 0;
}

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

函数模板特化

1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误

// 函数模板 -- 参数匹配
template
bool Less(T left, T right)
{
	return left < right;
}

// 对Less函数模板进行特化
template<>
bool Less(Date* left, Date* right)
{
	return *left < *right;
}

一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。

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

类模板的特化

1.全特化

基础的类模板:

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

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

//全特化
template<>
class Data
{
public:
	Data() { cout << "Data" << endl; }
private:
	int _d1;
	char _d2;
};

2.偏特化

基础的类模板:

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

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本

偏特化有两种表中形式:1.部分特化2.参数更进一步的限制

1.部分特化:

// 将第二个参数特化为int
template 
class Data
{
public:
	Data() { cout << "Data" << endl; }
private:
	T1 _d1;
	int _d2;
};

2.参数更进一步的限制

//两个参数偏特化为指针类型
template 
class Data 
{
public:
	Data() { cout << "Data" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

3.模板的分离编译

1.什么是分离编译

分离编译模式:一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程

2.模板的分离编译

2.1模板声明和定义分离为什么会导致链接错误?

我们先将模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义

//tfunc.h
template
T Add(const T& left, const T& right);

//func.cpp
template
T Add(const T& left, const T& right)
{
	return left + right;
}

//test.cpp
int main() 
{
	Add(1,2);
	Add(1.0,2.0);
	return 0;
}

效果:

这里看到链接错误了

但普通的函数时可以声明和定义分离的:

这是因为普通函数在编译的时候会被编译成指令

模板声明和定义分离后,不知道实例化出什么类型,没有被编译成指令

于是在链接的时候,找不到模板实例化出的函数Add的地址,因此报错

C++: 模板(进阶)_第1张图片

C++: 模板(进阶)_第2张图片

进程中有一个寄存器,用来记录当前执行到那一句指令,每一句指令都有一个地址

通过寄存器可以知道这段代码执行到了哪里,当这段程序执行时间到后,被切换出去,保存这个寄存器,等后面切换回来的时候,根据这个寄存器,来继续执行后面的代码

2.2为什么声明和定义放在一起后就不会链接错误了?

预处理的过程会将头文件展开,若声明定义放在一起,在test.cpp文件中展开后,调用该函数的时候,就会知道T的类型,知道这个函数的地址,会生成相关的指令

声明和定义放在一起后直接就可以实例化在编译的时候就有地址,不需要再连接

C++: 模板(进阶)_第3张图片

3.解决方法

1.显示实例化:

//func.h
template
T Add(const T& left, const T& right);

template
double Add(const double& left, const double& right);

template
int Add(const int& left, const int& right);

2.声明和定义放在一起

你可能感兴趣的:(C++,c++,开发语言)