◆博主名称:少司府

欢迎来到少司府的博客☆*: .。. o(≧▽≦)o .。.:*☆

数据结构系列个人专栏:

初阶数据结构_少司府的博客-CSDN博客

C++基础个人专栏:

C++初阶_少司府的博客-CSDN博客

琢玉成器终有时,笔底生花夺锦归

目录

一、非类型模板参数

        1.1 模板参数的分类

        1.2 array静态数组

二、模板的特化

        2.1 概念

        2.2 函数模板特化

        2.3 类模板特化

        2.3.1 全特化

        2.3.2 偏特化

三、模板分离编译

        3.1 什么是分离编译

        3.2 模板的分离编译

        3.3 链接错误解决方案

四、模板优缺点总结

        4.1 优点

        4.2 缺点


一、非类型模板参数

        1.1 模板参数的分类

        模板参数分类类型:类型形参非类型形参

类型形参即:出现在模板参数列表中,跟在 class 或者 typename 之类的参数类型名称
非类型形参,就是用一个常量作为类 ( 函数 ) 模板的一个参数,在类 ( 函数 ) 模板中可将该参数当成常 量来使用
template<size_t N = 10,bool flag = false,char s = ' '> // 非类型模板参数可以给缺省值
class Stack
{
private:
	int _a[N];
	int _top;
};

如图,非类型模板参数可以有缺省值,但是其只能为整型家族的值。

整型家族包括:char、int、size_t、long、long long、bool

也有例外:

// C++20支持double做模板参数
template<double D> 
class A
{
private:
	//...
};

C++20支持double作为模板参数。

注意:

1)、浮点数、类对象以及字符串是不允许非类型模板参数的。

2)、非类型模板参数必须在编译期就确认结果。

        1.2 array静态数组
array<int, 10> a1; // 不会初始化
array<int, 100> a2;

array是C++提供的一个静态数组,相比于自定义的数组,array检查越界更严格

	array<int, 10> a1; // 不会初始化
	array<int, 100> a2;
	int a3[10];
	// 越界检查问题
	cout << a3[10] << endl; //越界读不检查,越界写检查 
	//a3[10] = 10;
	 
	a1[10] = 10; // 越界读写都会检查
	cout << a1[10] << endl;

如图,自定义数组越界读不会报错,越界写会报错;array越界读写都会报错

二、模板的特化

        2.1 概念

        通常情况下,使用模板可以实现一些与类型无关的代码(比如模板函数),但对于一些特殊类型的可能会得到一些错误的结果,因此需要做特殊处理。

在原模板类的基础上,针对特殊类型所进行特殊化的实现方

模板特化分为函数模板特化和类模板特化。

        2.2 函数模板特化

函数模板特化的一般步骤:

1)、必须先有一个基础的函数模板

2)、关键字template后面接一对空尖括号<>

3)、函数名后跟一对尖括号,尖括号中指定需要特化的类型

// 函数模板 -- 参数匹配
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()
{
	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;
}

如图,我们可以通过模板的特化来实现对Date*的比较。当left、right类型为其他类型时,会走第一个函数模板;当其为Date*时,会走第二个。

当然,我们也可以直接使用现成的函数。

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

还有就是,我们在进行模板特化的时候,可能会遇到一些坑,比如:

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

// 不能写成const Date* left
template<>
bool Less<Date*>(Date* const& left, Date* const& right)
{
	return *left < *right;
}

注意const指向的内容,特化模板要与正常模板严格匹配,这里应该是底层const,是指向的内容不能修改,而不是指针本身不能修改

        2.3 类模板特化
        2.3.1 全特化

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

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; }
private:
	int _d1;
	char _d2;
};
        2.3.2 偏特化

        偏特化就是对参数列表中部分参数进行特化。

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

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

如图,当全特化与偏特化同时存在并实例化生效时,优先使用全特化

我们还可以对参数进行进一步的限制,例如:

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

针对所有类型的指针参数,都会用这个模板进行实例化。

三、模板分离编译

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

        3.2 模板的分离编译

        我们以下面情况为例:

// Func.h
void func(const int& left, const int& right);

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

// Func.cpp
template<class T>
T Add(const T& left, const T& right)
{
	cout << "Add(const T& left, const T& right)" << endl;
	return left + right;
}

void func(const int& left, const int& right)
{
	cout << "func(const int& left, const int& right)" << endl;
}

// Test.cpp
int main()
{
	Add(1, 2);
	Add(1.0, 2.0);

	func(1, 2);

	return 0;
}

如图,在链接阶段发生错误。在链接阶段,在符号表找到了func的地址,但是Add没有实例化,找不到地址。

        3.3 链接错误解决方案

方法一:在Func.cpp文件显示实例化

// 显式实例化
template
int Add(const int& left, const int& right);

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

方法二:直接在.h文件定义模板,用的地方就有定义,直接实例化。

四、模板优缺点总结

        4.1 优点

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

2)、增强了代码的灵活性

        4.2 缺点

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

2)、出现模板编译错误时,错误信息非常凌乱,不容易定位

本期的分享就到这里,如果觉得博主的文章比较对胃口的话,可以点一个小小的关注~

您的三连是我持续更新的动力~

更多推荐