模板进阶(仿函数,特化等介绍)

18 篇文章 0 订阅

非类型模板参数

模板参数有类型形参和非类型形参;

类型形参:使用typename或者class修饰的参数类型名称

非类型形参:一个普通常量作为模板参数形参,不能为浮点数,字符类型以及类对象;

#include<iostream>
using namespace std;

template<class T, size_t N = 64>
class test {

private:
	T data = 10;
public:
	void print()
	{
		cout << data << endl << N << endl;
	}

};


int main()
{

	test<int> a;
	a.print();
	return 0;
}

上面的代码中,T就是典型的类型形参,N为非类型形参,这个N我们在类函数成员中可以直接使用

模板的特化

我们都知道,使用模板能够很方便的做出能够适应各种场景的函数,比如比较大小,加减法等函数:


template<class T>
bool Less(T a, T b)
{
	return a <  b;
}

int main()
{
	cout << Less(1, 2) << endl;

	Date a(2022, 7, 8);
	Date b(2022, 7, 7);
	cout << Less(a, b)<< endl;



	return 0;
}

 

 

比如这样,就能够比较 int,double 之类的类型,但是有时候我们的类型可能无法满足我们的需求

 比如:

 我们需要比较的是Date类中的日期大小,但是我们传 Date* 类型进行比较时;

却发现比较的结果实际上是按照地址的大小来进行比较的。

对于这种情况,就需要我们的函数模板的特化出场了;

函数模板特化的步骤

1.有一个基础的函数模板

2.有一个template关键字,后面跟着一个空的<>

3.函数名后跟着一个<>,尖括号中指定需要特化的类型

4.函数形参表必须和模板函数的基础参数类型完全相同,否则会报错

比如上面的 Less 模板函数如何特化?

template<class T>
bool Less(T a, T b)
{
	return a <  b;
}

template<>
bool Less<Date*>(Date* a, Date* b)
{
	return *a < *b;
}

 我们对比基础函数模板,我们发现,特化的函数模板不仅template后面跟着空的 <> ,而且函数名后还有一个 <> ,并且里面有特化的类型,且形参名都是相同的

这样我们就能够顺利比较 Date* 类型的参数了;

 

即然有函数的模板特化,那么就有类的模板特化

类的模板特化和函数的模板特化的规则类似。

类的模板特化

类的模板特化分为两种——全特化和偏特化;

我们先了解简单的全特化

全特化

template<class T1,class T2>
class Test {
public:
	Test()
	{
		cout << "Test(T1,T2)" << endl;
	}

private:
	T1 a;
	T2 b;
};

template<>
class Test<char, char> {
public:
	Test()
	{
		cout << "Test(char,char)" << endl;
	}
};

int main()
{
	Test<int, int> t1;
	Test<char, char> t2;

	return 0;
}

全特化和函数特化一样,需要有基础的类模板,并且还得有其他一些条件才能实现特化;

若是创建的类对象和特化的类型一致,就会使用特化的类

偏特化

所谓偏特化就是指这个类并非全都是指定一个类型,而是有特化有没特化的。

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

 就比如这个,我们指定特化第二个参数是 int 类型的,因此若是后面使用 int 类型当做第二个模板参数,就会使用这个类型;

int main()
{
	Test<int, int> t1;
	Test<char, char> t2;
	Test<char, int> t3;
	return 0;
}

 

而偏特化里面还有一个更特殊的——针对模板参数进一步条件限制设计出来的特化版本

这个和普通的偏特化不同,它的规则不同,我们先来看看实例;

template<class T1,class T2>
class Test<T1&, T2&> {
public:
	Test()
	{
		cout << "Test(T1&,T2&)" << endl;
	}
};

我们发现,它的template后不是跟着空 <>,其内部有正常的模板参数;

但是类名后面跟着不同指定特化的类型。

这就是进一步限制条件的特化版本

模板特化应用示例

比如我们使用一个仿函数用来比较函数时,我们有时单纯靠模板无法满足需求,就需要特化版本来满足需求;

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

比如这个,我们比较普通的类型无所谓,但是若是比较的是 Date* 的类型时,就会比较地址大小导致出错,因此需要特化。

template<>
class Less<Date*>
{
	bool operator()(const Date* x, const Date* y)
	{
		return *x < *y;
	}
};

模板分离编译

当我们写模板类的时候需要注意一个点——模板类不能分离编译。

什么是分离编译?就是指模板函数或者模板类的函数声明和定义分别在不同的文件

而模板函数和模板类的声明和定义在不同位置会导致出错。

这是因为编译器对于每个源文件都是独立编译的,若是分离编译则会导致源文件之间没有交互,导致对应的模板没有实例化,从而出错。

具体解决办法就是将声明定义放在一起,从而避免出错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值