C++ 重载函数调用运算符 | 再探lambda,函数对象,可调用对象

文章目录

  • 重载函数调用运算符
  • lambda
    • lambda等价于函数对象
    • lambda等价于类
  • 标准库函数对象
  • 可调用对象与function
    • 可调用对象
    • function
      • 函数重载与function


重载函数调用运算符

函数调用运算符必须是成员函数。 一个类可以定义多个不同版本的调用运算符,互相之间应该在参数数量or类型上有所区别。如果类定义了函数调用运算符 () ,则该类的对象称作函数对象,因为可以调用这种对象,所以我们说这些对象的行为像函数一样。

class absInt {
public:int operator()(int val)const {return val < 0 ? -val : val;}
};

在这里插入图片描述


lambda

lambda等价于函数对象

对于作为最后一个实参的 lambda 的表达式来说:

//根据单词的长度对其进行排序,对于长度相同的单词按照字母表顺序排序
stable_sort (words.begin(), words.end(),[](const string &a, const string &b){return a.size () < b.size();});

其等价于下面这个类的一个未命名对象:

class ShorterString {
public:bool operator()(const string &s1, const string &s2) const{ return s1.size() < s2.size(); }
};

默认情况下,lambda 不能改变它捕获的变量,因此类的函数调用运算符是一个 const 成员函数。

那么我们可以重写并调用 stable_sort

stable_sort(words.begin(), words.end(), ShorterString());

lambda等价于类

  • 当一个 lambda表达式 通过引用捕获变量时,将由程序负责确保 lambda 执行时所引用的对象确实存在。因此,编译器可以直接使用该引用而无须在 lambda产生的类 中将其存储为数据成员。
  • 过值捕获的变量被拷贝到 lambda 中。因此,这种 lambda 产生的类必须为每个值捕获的变量建立对应的数据成员,同时创建构造函数,令其使用捕获的变量的值来初始化数据成员。

举个例子,有这样一个 lambda,它的作用是找到第一个长度不小于给定值的 string 对象:

//获得第一个指向满足条件元素的迭代器,该元素满足 size() >= sz
auto wc = find_if(words.begin(), words.end(),[sz] (const string &a){ return a.size() >= sz;});

等价于这样的类:

class SizeComp{size_t sz; // 该数据成员对应通过值捕获的变量
public:SizeComp (size_t n): sz(n) {} // 该形参对应捕获的变量// 该调用运算符的返回类型、形参和函数体都与lambda一致bool operator()(const string &s) const { return s.size()>= sz; }

由于这个类不含默认构造函数,因此想要调用它必须提供一个实参,也就起到了 值捕获 的作用。


标准库函数对象

在这里插入图片描述
表示运算符的函数对象类常用来替换算法中的默认运算符。

例如,在默认情况下排序算法使用 operator< 将序列按照升序排列。如果要执行降序排列的话,我们可以传入一个 greater 类型的对象。该类将产生一个调用运算符并负责执行待排序类型的大于运算:

// sv是一个vecotr<string>,greater<string>()是一个临时的函数对象
sort(sv.begin(), sv.end(), greater<string>());

标准库函数对象适用性是很宽的,甚至适用于指针:
在这里插入图片描述
而实际上,STL 中也正是这样做的:关联容器使用 less<key_type> 对元素排序,因此我们可以定义一个指针的 set 或者在 map 中使用指针作为关键值而无须直接声明 less


可调用对象与function

可调用对象

C++语言中有几种可调用的对象:函数、函数指针、lambda表达式、bind创建的对象、重载了函数调用运算符的类。

和其他对象一样,可调用的对象也有类型。例如,每个lambda有它自己唯一的(未命名)类类型;函数及函数指针的类型则由其返回值类型和实参类型决定,等等。

然而,两个不同类型的可调用对象却可能共享同一种调用形式(call signature)。 调用形式指明了调用返回的类型以及传递给调用的实参类型。一种调用形式对应一个函数类型,例如:

int(int, int)

是一个函数类型,它接受两个 int、返回一个 int

用具体的例子来讲,即:
在这里插入图片描述
上面三个可调用对象共享一种调用形式—— int(int, int)

对于调用形式相同的可调用对象,我们可以定义一个函数表(function table)存储他们的“指针”,对他们进行统一管理。 当程序需要执行特定的 +%/ 时,从表中查找该操作对应的可调用对象。

C++ 中,函数表很容易通过 map 实现:

// string表示 + 或 % 或 / 
// 函数指针 作为 value
map<string, int(*)(int, int)> m;
// 我们可以将 add的指针 加入到 m 中
m.insert({"+", add}); // {"+", add}是一个pair
// 但不可以将 mod 或者 divide 存入 m
m.insert({"%", mod}); // error:mod不是一个函数指针,lambda有自己的类类型 

function

在这里插入图片描述
function 模板的运用示例:

function<int(int, int)>
// 声明了一个function类型,表示接受两个int、返回一个int的可调用对象

在这里插入图片描述

// 用function<int(int, int)>表示上面的可调用对象
function<int(int, int)> f1 = add; // 函数指针
function<int(int, int)> f2 = divide(); // 函数对象类的对象
function<int(int, int)> f3 = [](ing i, int j) {return i%j}; // lambdacout << f1(4, 2) << endl; // 打印6
cout << f2(4, 2) << endl; // 打印2
cout << f3(4, 2) << endl; // 打印0

function 也可以和 map 搭配使用:

初始化:
在这里插入图片描述
使用:
在这里插入图片描述


函数重载与function

我们不能直接将重载函数的名字存入 function 类型的对象中:

int add(int i, int j) { return i + j; }
A add(const A&, const A&);
map<string, function<int(int, int)>> m;
m.insert( {"+", add} ); // ERROR:哪个add?

解决上述二义性问题的一条途径是存储函数指针而非函数名字:

int (*fp)(int, int) = add;
m.insert( {"+", fp} );

新版本标准库中的 function 类与旧版本中的 unary_function 和 binary_function 没有关联。后两个类已经被更通用的 bind 函数替代了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/443735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 运算符重载(二) | 类型转换运算符,二义性问题

文章目录类型转换运算符概念避免过度使用类型转换函数解决上述问题的方法转换为 bool显式的类型转换运算符类型转换二义性重载函数与类型转换结合导致的二义性重载运算符与类型转换结合导致的二义性类型转换运算符 概念 类型转换运算符&#xff08;conversion operator&#…

Tomcat中JVM内存溢出及合理配置

Tomcat本身不能直接在计算机上运行&#xff0c;需要依赖于硬件基础之上的操作系统和一个Java虚拟机。Tomcat的内存溢出本质就是JVM内存溢出&#xff0c;所以在本文开始时&#xff0c;应该先对Java JVM有关内存方面的知识进行详细介绍。 一、Java JVM内存介绍 JVM管理两种类型的…

俄罗斯农民乘法 | 快速乘

文章目录概念概念 俄罗斯农民乘法经常被用于两数相乘取模的场景&#xff0c;如果两数相乘已经超过数据范围&#xff0c;但取模后不会超过&#xff0c;我们就可以利用这个方法来拆位取模计算贡献&#xff0c;保证每次运算都在数据范围内。 A 和 B 两数相乘的时候我们如何利用加…

Linux网络编程 | socket选项设定 及 网络信息API

文章目录读取和设置 socket 选项SO_REUSEADDRSO_RCVBUF 和 SO_SNDBUFSO_RCVLOWAT 和 SO_SNDLOWATSO_LINGER 选项网络信息APIgethostbyname 和 gethostbyaddrgetservbyname 和 getservbyportgetaddrinfogetnameinfo读取和设置 socket 选项 正如 fcntl 系统调用是控制文件描述符…

Linux | 高级I/O函数

文章目录创建文件描述符的函数pipe函数dup函数、dup2函数读取或写入数据readv函数、writev函数零拷贝sendfile函数splice函数tee函数进程间通信——共享内存mmap函数 和 munmap函数控制文件描述符fcntl函数创建文件描述符的函数 pipe函数 不再赘述&#xff0c;详情见我的另一…

分布式理论:CAP、BASE | 分布式存储与一致性哈希

文章目录分布式理论CAP定理BASE理论分布式存储与一致性哈希简单哈希一致性哈希虚拟节点分布式理论 CAP定理 一致性&#xff08;Consistency&#xff09;&#xff1a; 在分布式系统中的所有数据副本&#xff0c;在同一时刻是否一致&#xff08;所有节点访问同一份最新的数据副…

Tomcat服务器性能优化

一、概述 本文档主要介绍了Tomcat的性能调优的原理和方法。可作为公司技术人员为客户Tomcat系统调优的技术指南&#xff0c;也可以提供给客户的技术人员作为他们性能调优的指导手册。 二、调优分类 由于Tomcat的运行依赖于JVM&#xff0c;从虚拟机的角度我们把Tomcat的调整分为…

分布式系统概念 | 分布式事务:2PC、3PC、本地消息表

文章目录分布式事务2PC&#xff08;二阶段提交协议&#xff09;执行流程优缺点3PC&#xff08;三阶段提交协议&#xff09;执行流程优缺点本地消息表&#xff08;异步确保&#xff09;分布式事务 分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分…

数据结构算法 | 单调栈

文章目录算法概述题目下一个更大的元素 I思路代码下一个更大元素 II思路代码132 模式思路代码接雨水思路算法概述 当题目出现 「找到最近一个比其大的元素」 的字眼时&#xff0c;自然会想到 「单调栈」 。——三叶姐 单调栈以严格递增or递减的规则将无序的数列进行选择性排序…

最长下降子序列

文章目录题目解法DP暴搜思路代码实现贪心二分思路代码实现题目 给出一组数据 nums&#xff0c;求出其最长下降子序列&#xff08;子序列允许不连续&#xff09;的长度。&#xff08;类似于lc的最长递增子序列&#xff09; 示例&#xff1a; 输入&#xff1a; 6 // 数组元素个…

Linux 服务器程序规范、服务器日志、用户、进程间的关系

文章目录服务器程序规范日志rsyslogd 守护进程syslog函数openlog函数setlogmask函数closelog函数用户进程间的关系进程组会话系统资源限制改变工作目录和根目录服务器程序后台化服务器程序规范 Linux 服务器程序一般以后台进程&#xff08;守护进程[daemon]&#xff09;形式运…

IO模型 :阻塞IO、非阻塞IO、信号驱动IO、异步IO、多路复用IO

文章目录IO模型阻塞IO非阻塞IO信号驱动IO多路复用IO异步IOIO模型 根据各自的特性不同&#xff0c;IO模型被分为阻塞IO、非阻塞IO、信号驱动IO、异步IO、多路复用IO五类。 最主要的两个区别就是阻塞与非阻塞&#xff0c;同步与异步。 阻塞与非阻塞 阻塞与非阻塞最主要的区别就…

Tomcat服务器集群与负载均衡实现

一、前言 在单一的服务器上执行WEB应用程序有一些重大的问题&#xff0c;当网站成功建成并开始接受大量请求时&#xff0c;单一服务器终究无法满足需要处理的负荷量&#xff0c;所以就有点显得有点力不从心了。另外一个常见的问题是会产生单点故障&#xff0c;如果该服务器坏掉…

Linux服务器 | 事件处理模式:Reactor模式、Proactor模式

文章目录Reactor模式Proactor模式同步I/O模型模拟Proactor模式两者的优缺点ReactorProactor同步I/O模型通常用于实现 Reactor 模式&#xff0c;异步I/O模型通常用于实现 Proactor 模式。&#xff08;不是绝对的&#xff0c;同步I/O也可模拟出 Proactor 模式&#xff09; React…

Linux服务器 | 服务器模型与三个模块、两种并发模式:半同步/半异步、领导者/追随者

文章目录两种服务器模型及三个模块C/S模型P2P模型I/O处理单元、逻辑单元、存储单元并发同步与异步半同步/半异步模式变体&#xff1a;半同步/半反应堆模式改进&#xff1a;高效的半同步/半异步模式领导者/追随者模式组件 &#xff1a;句柄集、线程集、事件处理器工作流程两种服…

香农信息熵之可怜的小猪

文章目录题目解析香农熵公式样例具体分析代码题目 有 n 桶液体&#xff0c;其其中 正好 有一桶含有毒药&#xff0c;其装的都是水。它们从外观看起来都一样。为了弄清楚哪只水桶含有毒药&#xff0c;你可以喂一些猪喝&#xff0c;通过观察猪是否会死进行判断&#xff0c;实验对…

字符串匹配之KMP(KnuthMorrisPratt)算法(图解)

文章目录最长相等前后缀next数组概念代码实现图解GetNext中的回溯改进代码实现代码复杂度分析最长相等前后缀 给出一个字符串 ababa 前缀集合&#xff1a;{a, ab, aba, abab} 后缀集合&#xff1a;{a, ba, aba, baba} 相等前后缀 即上面用同样颜色标识出来的集合元素&#…

linux下tomcat6.0与jdk安装详细步骤

安装Tomcat6.0和JDK1.6 在linux系统上安装tomcat和jdk应该说是我学习linux知识的第一课了&#xff0c;之前只 是听说过&#xff0c;从没接触过&#xff0c;但我们公司项目都是部署在linux系统上的&#xff0c;那天上司突 然给我发了几个文档&#xff0c;让我看一下&#xff…

Android入门(一) | Android Studio的配置与使用

文章目录安装配置Android Studio使用Android Studio模拟器更改Android SDK的路径Hello World&#xff01;安装配置Android Studio 从这一步开始&#xff1a; 一直点 next 即可&#xff0c;直到存储路径的选择上&#xff0c;可以放到非 C 盘&#xff0c;这里我放到 D 盘了&am…

Android 入门(四) | Intent 实现 Activity 切换

文章目录Intent显式 Intent定义两个 xml 文件android:orientationmatch_parent 和 wrap_contentIntent函数定义两个 Activity隐式 Intent更多隐式 Intent 的用法用隐式 Intent 打开系统浏览器自建 Activity 以响应打开网页的 Intent向下一个活动传递数据返回数据给上一个活动In…