【C++进阶(七)】仿函数深度剖析&模板进阶讲解-LMLPHP

1. 前言

C++进阶中关于STL库的初级数据
结构就已经结束了,高阶数据结构如:
二叉搜索树AVL树,红黑树,哈希
等等将在C++高阶讲解.


2. 仿函数的概念

仿函数的本质就是一个类,此类中
运算符重载了括号()!所以它使用起来
和函数很相似,就叫做仿函数

【C++进阶(七)】仿函数深度剖析&模板进阶讲解-LMLPHP

class Less
{
public:
	bool operator()(int x,int y)
	{
		return x<y;
	}
};
int main()
{
	Less functor;
	cout<<functor(1,2);
}

注:1小于2,会返回true,打印1


3. 仿函数的实际用途

首先是使用库中的某些函数时
仿函数能很方便的改变升降序或大小堆

vector<int> v{9,8,7,6};
sort(v.begin(),v.end());
sort(v.begin(),v.end(),less<int>);
vector<int> v{6,7,8,9};
sort(v.begin(),v.end(),greater<int>);
priority_queue<int> p1;
priority_queue<int,vector<int>,less<int>> p2;
priority_queue<int,vector<int>,greater<int>> p;

注:优先级队列的适配器参数在仿函数
前面,想要显示传仿函数,先要穿前面的

当然,greater的内部实现和less
只差了一个符号而已,如下:

class Greater
{
public:
	bool operator()(int x,int y)
	{
		return x>y;
	}
};

4. 模板的非类型模板参数

template<class T,int N = 10>
class test
{
	T a[N];
};

test<int,50> t1;
test<double> t2;

注:N=10是缺省值,没传时默认为10

【C++进阶(七)】仿函数深度剖析&amp;模板进阶讲解-LMLPHP

【C++进阶(七)】仿函数深度剖析&amp;模板进阶讲解-LMLPHP

array是静态数组
也就是固定大小的顺序容器
使用时,要显示传参N来初始化数组


5. 模板的特化简单介绍

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

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

Less绝对多数情况下都可以正常比较
但是在特殊场景下就得到错误的结果

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

类模板分为函数模板和类模板


6. 函数模板深度剖析

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
 return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less<int*>(int* left, int* right)
{
	return *left < *right;//比较指针指向的内容
}

当传参时给函数传了int类型的指针
那么就不会调用第一个函数,而是走
第二个特化的函数,特化也就是特殊处理

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

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

所以实际上函数模板的特化是不常用的


7. 类模板的特化深度剖析

类模板的特化分为全特化和偏特化

7.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;
};
Data<int, int> d1;
Data<int, char> d2;

和函数模板特化一样,特化的部分
要加上template<>作为格式,上面
初始化时,<int,int>类型不会走模板特化
然而<int,char>类型会走模板特化


7.2 模板的偏特化

然而偏特化又有两种表现形式:

  • 部分特化
  • 对参数做进一步限制
// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
	Data() {cout<<"Data<T1, int>" <<endl;}
private:
	T1 _d1;
	int _d2;
};
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{ 
public:
	Data() {cout<<"Data<T1*, T2*>" <<endl;}
private:
	T1 _d1;
	T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
	: _d1(d1)
	, _d2(d2)
	{
		cout<<"Data<T1&, T2&>" <<endl;
	}
private:
	const T1 & _d1;
	const T2 & _d2; 
 };

8. 总结以及拓展

补充完仿函数和模板进阶相关知识后
接下来我们将进入继承和多态的学习
继承和多态这部分在校招中考察的很多
请耐心学习~~

为什么模板不能分离编译?
模板分离编译问题剖析


10-04 10:42