C++初阶 | [十二] 模板进阶

本文详细探讨了C++中的模板技术,包括类型模板参数的使用,非类型模板参数的应用,类模板和函数模板的特化,以及模板的分离编译。文章还提到std::array的作用以及模板在提高代码复用性和灵活性的同时,带来的代码膨胀和编译错误问题。
摘要由CSDN通过智能技术生成

摘要:非类型模板参数,类模板的特化,模板的分离编译,模板总结

前言:C++初阶终篇


1. 非类型模板参数

类型模板参数:如下代码,T 为模板的类型参数。 

#define N 10
template<class T>
class A
{
public:

private:
	T* _a[N];
};

void test_1()
{
	A<int> a1;
	A<double> a2;
}

非类型模板参数:对于上述代码,如果我们想要对于不同的数据类型创建不同大小的_a数组呢?例如,对于 A<int> 我们可能只要在其中存储10个 int 数据,而 A<double> 我们可能需要在其中存储 100 个 double 类型的数据。所以,对于这样的需求,我们可以在模板中加入非类型模板参数来满足。

template<class T, size_t N>
class A
{
public:

private:
	T* _a[N];
};

void test_1()
{
	A<int, 10> a1;
	A<double, 100> a2;
}

如上代码,其中的 size_t N 即为模板中的非类型模板参数
对于非类型模板参数有两个要求:(ps. char 也可以,char 也是属于整型家族。另外,C++98只支持整型,C++98之后支持非整型)

(补充) array

std::array
------------------------------------------
template<class T, size_t N> class array;

cplusplus.com/reference/array/array/

array 的意义: 

  • C语言:对于 int a[10];  a[15] = 1; 不会检查越界,a[15] 这个过程本质上是指针解引用
  • C++:对于 array<int ,10> a2; a2[15] = 1; 会检查越界,这里的 a2[15] 本质上是函数调用,所以可以在函数里进行执行越界检查。

C++11初衷是希望大家用这个来代替静态数组,但其实有更好的选择——vector。vector不仅对于越界设置了检查,而且可以按需求进行初始化(array不能按自己需求初始化)


2. 模板的特化

特化:针对某些类型做特殊处理

1)类模板的特化

  • 全特化 

e.g.

上图所展示的即为类模板的全特化,即针对 Data 类中 int 和 char 类型做特殊处理。

#include<iostream>

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

template<>
class Data<int, char>
{
public:
	Data()
	{
		std::cout << "Data<int, char>" << std::endl;
	}
private:
	int _d1;
	char _d2;
};


void test_2()
{
	Data<int, int> d1;
	Data<int, char> d2;
}

int main()
{
	//test_1();
	test_2();
	return 0;
}

执行上述代码的结果为:

Data<T1, T2>
Data<int, char>

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

  • 偏特化

(也称半特化)

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

(针对上述代码所写的 Data 类所举出的例子) 

  1. 方式一:将模板参数类表中的一部分参数特化。

    template<class T1 , class T2>
    class Data
    { …… };


    template<>
    class Data<T1,double>
    { …… };

     
  2. 方式二针对模板参数更进一步的条件限制

    template<class T1 , class T2>
    class Data
    { …… };


    template<>
    class Data<T1*,T2*>
    { …… }; //这里的意思是只要的类型为指针(例如:int* , double* , char* , ……)就调用这个特化出来的 class Data 

2)函数模板的特化 

引入:在 优先级队列 的文章中我们讨论了 对于 priority_queue<Date*> 如何通过仿函数来实现“自定义比较的规则”。这里,我们可以选择通过函数模板的特化来实现。即对 class Less 实现特化。

但是,对于上图所展示的代码,class Less 的成员函数的参数列表按上图的写法会发生传值拷贝,传值拷贝对于自定义类型可能会有深浅拷贝的问题。 因此,参数列表建议改成 cosnt 引用,然而,这同时会给函数模板特化带来一些小问题。

(如果你需要上图的代码↓) 

template<class T>
class Less
{
	bool operator()(const T& left, const T& right)
	{
		return left < right;
	}
};


template<>
class Less<Date*>
{
	bool operator()(Date* const& left, Date* const& right)
	{
		return *left < *right;
	}
};

 3)sum.

如果需要,类模板可以使用特化,而函数模板一般不使用特化,更好的选择是实现重载。相当于自己实例化出来一个针对某个数据类型的函数,而自己实例化的这个函数会与函数模板根据类型推导由编译器自动生成的实例化出来的函数构成重载。


3. 模板的分离编译

1)函数模板

2)类模板

类模板的分离编译一般是头文件声明成员(成员变量和成员函数),另外的 .cpp 文件中定义成员函数,以这样的形式分离。同样会产生如函数模板分离编译的链接错误。

3)sum.

如果声明和定义都在一个头文件里,那么预处理展开之后就相当于 调用定义 都在同一个文件里,编译的时候就可以通过调用语句“告诉”编译器通过模板的定义怎样实例化。

(ps.如果非得使模板的声明和定义分离,可以 不 同时 声明和定义,但最好要把声明和定义放在一个文件里)


4. 模板总结

【优点】

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

2. 增强了代码的灵活性。

【缺陷】

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

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


END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

畋坪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值