C++:模板初阶

函数模板

功能:

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

也就是说,我们可以通过一个函数模板,让编译器生成一整个同类型的函数家族。

语法:

template <typename T1, typename T2 ......>
template <class T1, class T2 ......>

示例:

template <class T>
T Add(T x, T y)
{
	return x + y;
}

编译器会根据调用函数时传入的参数,自动判断类型

模板只是一个蓝图,本身不是函数,当我们传入指定类型参数,其就会生成相应的函数。 

显式实例化 

语法:

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

比如:

int a = 5;
double b = 3.0;
Add<int> (a, b);
Add<double> (a, b);
模板参数缺省

在设置模板参数时,可以设置缺省值,在显式实例化时,对于没有指明的模板参数,会被推演为缺省值。

给模板参数缺省值试试:

template <class T1, class T2 = double>
void func(T1 a)
{
	T2 b = 5;
	cout << a / b;
}
参数匹配规则

模板本身不是一个函数,所以同名的函数和模板是可以共存的。

template <class T>
T func(T x, T y)
{
	return x + y;
}

int func(int x, int y)
{
	return x + y;
}

调用函数时,如果函数有现成的,完全匹配的函数,那么不会调用模板。

调用函数时,如果可以通过模板产生更加匹配的函数,那么会调用模板进行推演

类模板

类模板的特性与函数模板几乎一致,此处不额外讲解了,只讲解类模板的特殊的地方。

语法:

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

我们的模板参数为T,由于类没有传参的概念,不能通过参数来推演类型,所以一般而言类的模板都是要显式实例化的。

比如:

stack<int> s1;
stack<double> s2;
类成员的声明定义分离

当我们希望把一些类中的成员定义在类的外部时,那就需要声明和定义分离。

假设我们希望分离析构函数~stack
对于一般的类,我们会这样分离:

class stack
{
public:
	stack(size_t capacity = 10)
	:_pData(new int[capacity])
	,_size(0)
	,_capacity(capacity)
	{}
	
	~stack();//声明
	
private:
	int* _pData;
	size_t _size;
	size_t _capacity;
};

stack::~stack()
{
	//函数体
}

所以我们的类模板也要类型::函数名来限定作用域。类模板的类型刚刚介绍过,就是stack<T>,所以函数的声明应该这样写:

stack<T>::~stack()
{
	//函数体
}

对于类模板,当在类外定义函数时,要添加模板参数列表。 

也就是说要这样:

template <class T>
stack<T>::~stack()
{
	//函数体
}

非类型模板参数

我们的模板参数也可以不是一个类型,而是一个数值。

对于指定类型的参数,我们称为类型形参,比如int,double。
对于一个数值的参数,我们称为非类型形参。

template <class T, int N>
class Array
{
public:

private:
	T _arr[N];
};

非类型模板参数必须是整型,bool,char类型的常量

模板特化

通常情况下,使用模板可以实现一些与类型无关的代码,但是对于一些特殊的类型,有可能会得到错误的结果。

template<class T>
bool Less(T x, T y)
{
	return x < y;
}

int main()
{
	int* p1 = 5;
	int* p2 = 10;
	cout << Less(p1, p2) << endl;
	return 0;
}

这个函数模板中,我们用Less来比大小,我们此时传入了两个指针p1p2,原本的意图是通过指针来比较数字5和10的大小。但是当传入后,我们比较的是p1 < p2,也就是对两个指针比大小了,这不符合我们的预期。也就是说在面对指针的时候,我们需要特殊处理,这就需要模板特化了。

模板特化的功能就是:

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

函数模板特化

我们先看到一个函数模板特化,再讲解语法:

//基础模板
template<class T>
bool Less(T x, T y)
{
	return x < y;
}

//模板特化
template<>
bool Less<int*>(int* x, int* y)
{
	return *x < *y;
}

第一段代码是一般的函数模板,而第二段是对int进行了特化的版本,当我们传入参数类型为int时,就会调用这个特化版本,执行*x < *y,先解引用再比较。

首先,我们将T特化为了int*,所以T不再是一个需要推演的参数了,此时将T从模板参数列表中抽离出来,改为int*放到函数名Less后面,用尖括号括起来,然后把函数参数中所有的T改为特化后的int*

模板特化要满足以下语法:

  1. 必须存在一个基础模板
  2. 对于特化版本,template后面的<>内部不写被特化的模板参数
  3. 对于特化版本,在函数名后跟上<>,内部指定特化类型
  4. 将特化前的模板参数改为特化后的具体类型。

类模板特化

模板特化要满足以下语法:

  1. 必须存在一个基础模板
  2. 对于特化版本,template后面的<>内部不写被特化的模板参数
  3. 对于特化版本,在函数名后跟上<>,内部指定特化类型
  4. 将特化前的模板参数改为特化后的具体类型
  5. 类模板特化分为全特化和偏特化。
全特化

全特化是指将模板参数的所有参数都确定下来

//基础模板
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;
};
偏特化

偏特化是指并没有把模板参数确定下来,但是对满足特定条件的模板参数,执行特化版本

 偏特化分为部分特化和限制特化:

部分特化

部分特化是只将一部分参数特化

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

//模板特化
template<class T1>
class Data<T1, char>
{
public:
	Data() {cout<<"Data<T1, char>" <<endl;}
private:
	T1 _d1;
	char _d2;
};
限制特化

限制特化是对参数进行条件限制,但是没有把参数类型确定下来

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

//模板特化
template<class T1, class T2>
class Data<T1*, T2*>
{
public:
	Data() {cout<<"Data<T1*, T2*>" <<endl;}
private:
	T1 _d1;
	T2 _d2;
};

此处<T1*, T2*>是限定:当T1T2为指针是,调用此模板特化。
也就是说,这个过程中,T1T2的类型是不确定的,任然需要推演,所以第一行的模板参数列表保留了T1T2。 

 而这样对模板参数进行限制,就是限制特化了。

模板分离编译

解决方法  

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

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

模板总结  
【优点】
1. 模板复用了代码,节省资源,更快的迭代开发, C++ 的标准模板库 (STL) 因此而产生
2. 增强了代码的灵活性
【缺陷】
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

  • 21
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值