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

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

1. 前言

C++进阶中关于STL库的初级数据

结构就已经结束了,高阶数据结构如:

二叉搜索树AVL树,红黑树,哈希

等等将在C++高阶讲解.

本章重点:

本篇文章着重讲解仿函数的概念
以及自行实现一个仿函数.模板进阶
中,着重讲解非类型模板参数,模板
的特化还有模板的分离编译


2. 仿函数的概念

仿函数的本质就是一个类,此类中

运算符重载了括号()!所以它使用起来

和函数很相似,就叫做仿函数

在标准库的优先级队列的类模板中
有这样一个缺省参数叫less:

这个less就是一个仿函数,它会将
优先级队列变成大堆,在算法库的
sort函数默认是升序,其实就是用的less
与less对应的仿函数是greater,greater
可以将优先级队列变成小堆,将sort变成降序

我们可以模仿实现一下less的使用场景:

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. 模板的非类型模板参数

模板参数类型解析:

模板参数分类类型形参与非类型形参
类型形参即:出现在模板参数列表中
跟在class/typename之后的参数类型
非类型形参,就是用一个常量作为
类(函数)模板的一个参数,在类(函数)
模板中可将该参数当成常量来使用

比如:

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

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

讲到这儿就不得不介绍STL中一个不常用的容器了

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

array属于C++的数组,使用array
时,不管是越界读还是越界写都能
被检测到从而报错,然而使用C语言
的数组时,越界读写不一定会报错


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;
};

此时,只要第二个参数是int,就会

走偏特化,第二个参数不是int就不走

对上面的类做参数进一步限制:

//两个参数偏特化为指针类型
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. 总结以及拓展

补充完仿函数和模板进阶相关知识后

接下来我们将进入继承和多态的学习

继承和多态这部分在校招中考察的很多

请耐心学习~~

对于模板分离编译的拓展:

为什么模板不能分离编译?

模板分离编译问题剖析


🔎 下期预告:C++继承 🔍


目录
打赏
0
0
0
0
18
分享
相关文章
模板(C++)
本内容主要讲解了C++中的函数模板与类模板。函数模板是一个与类型无关的函数家族,使用时根据实参类型生成特定版本,其定义可用`typename`或`class`作为关键字。函数模板实例化分为隐式和显式,前者由编译器推导类型,后者手动指定类型。同时,非模板函数优先于同名模板函数调用,且模板函数不支持自动类型转换。类模板则通过在类名后加`&lt;&gt;`指定类型实例化,生成具体类。最后,语录鼓励大家继续努力,技术不断进步!
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
|
2月前
|
【c++】模板详解(2)
本文深入探讨了C++模板的高级特性,包括非类型模板参数、模板特化和模板分离编译。通过具体代码示例,详细讲解了非类型参数的应用场景及其限制,函数模板和类模板的特化方式,以及分离编译时可能出现的链接错误及解决方案。最后总结了模板的优点如提高代码复用性和类型安全,以及缺点如增加编译时间和代码复杂度。通过本文的学习,读者可以进一步加深对C++模板的理解并灵活应用于实际编程中。
41 0
深入理解C++模板编程:从基础到进阶
在C++编程中,模板是实现泛型编程的关键工具。模板使得代码能够适用于不同的数据类型,极大地提升了代码复用性、灵活性和可维护性。本文将深入探讨模板编程的基础知识,包括函数模板和类模板的定义、使用、以及它们的实例化和匹配规则。
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
179 4
【C++】模板详细讲解(含反向迭代器)
C++模板是泛型编程的核心,允许编写与类型无关的代码,提高代码复用性和灵活性。模板分为函数模板和类模板,支持隐式和显式实例化,以及特化(全特化和偏特化)。C++标准库广泛使用模板,如容器、迭代器、算法和函数对象等,以支持高效、灵活的编程。反向迭代器通过对正向迭代器的封装,实现了逆序遍历的功能。
73 3
|
5月前
|
【c++】模板详解(1)
本文介绍了C++中的模板概念,包括函数模板和类模板,强调了模板作为泛型编程基础的重要性。函数模板允许创建类型无关的函数,类模板则能根据不同的类型生成不同的类。文章通过具体示例详细解释了模板的定义、实例化及匹配原则,帮助读者理解模板机制,为学习STL打下基础。
66 0
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
38 12
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
48 16

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等