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&#…

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

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

分布式系统概念 | 分布式事务: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;句柄集、线程集、事件处理器工作流程两种服…

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

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

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…

Android入门(二) | 项目目录及主要文件作用分析

文章目录项目目录分析app目录分析AndroidManifest.xml 分析MainActivity.kt 分析build.gradle 分析最外层目录下的 build.gradleapp 目录下的 build.gradle项目目录分析 我们来看一下 src/main/res 下的一些文件&#xff1a; .gradle 和 .idea &#xff1a;这两个目录下放置…

Android入门(三) | Android 的日志工具 Logcat

文章目录日志工具类 android.util.LogLogcat 中的过滤器日志工具类 android.util.Log Log 从属日志工具类 android.util.Log &#xff0c;该类提供了五个方法供我们打印日志&#xff1a; Log.v() &#xff1a;用于打印那些最为琐碎的、意义最小的日志信息。对应级别 verbose&…

Android入门(五) | Activity 的生命周期

文章目录Activity 的状态及生命周期实现管理生命周期FirstActivitySecondActivityDialogActivity运行结果旧活动被回收了还能返回吗&#xff1f;Activity 的状态及生命周期 Android 的应用程序运用 栈&#xff08;Back Stack&#xff09; 的思想来管理 Activity&#xff1a; …

Android入门(六) | Activity 的启动模式 及 生产环境中关于 Activity 的小技巧

文章目录Activity 的启动模式standardsingleTopsingleTasksingleInstance技巧了解当前界面是哪个 Activity随时随地退出程序启动活动的最佳写法Activity 的启动模式 standard&#xff1a;默认的启动方式&#xff0c;每次启动一个活动都会重新创建singleTop&#xff1a;如果该活…

Android入门(七) | 常用控件

文章目录TextView 控件&#xff1a;文本信息Button 控件&#xff1a;按钮EditText 控件&#xff1a;输入框ImageView 控件&#xff1a;图片ProgressBar 控件&#xff1a;进度条AlertDialog 控件&#xff1a;提示框ProgressDialog 控件&#xff1a;带有进度条的提示框TextView 控…

Android入门(八) | 常用的界面布局 及 自定义控件

文章目录LinearLayout &#xff1a;线性布局android:layout_gravity &#xff1a;控件的对齐方式android:layout_weight&#xff1a;权重RelativeLayout &#xff1a;相对布局相对于父布局进行定位相对于控件进行定位边缘对齐FrameLayout &#xff1a;帧布局Percent &#xff1…

Android入门(九)| 滚动控件 ListView 与 RecyclerView

文章目录ListView内置类型的简单运用定制数据类型提升效率点击事件RecyclerView布局管理器点击事件ListView 内置类型的简单运用 由于手机屏幕空间有限&#xff0c;能够一次性在屏幕上显示的内容不多&#xff0c;当我们的程序有大量数据需要显示的时候就可以借助 ListView 来…