C++function

可调用对象与function

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

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

然而,两个不同类型的可调用对象却可能共享同一种调用形式。

调用形式指明了调用返回的类型以及传递给调用的实参类型。

一种调用形式对应一个函数类型,例如:

int(int, int)

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

不同类型可能具有相同的调用形式

对于几个可调用对象共享同一种调用形式的情况,有时我们会希望把它们看成具有相同的类型。

例如,考虑下列不同类型的可调用对象:

//普通函数
int add(int i, int j) { return i + j;}

// lambda,其产生一个未命名的函数对象类
auto mod = [](int i, int j) { return i % j; };
//函数对象类

struct divide
{
int operator() (int denominator, int divisor){
return denominator / divisor;
}
};

上面这些可调用对象分别对其参数执行了不同的算术运算,尽管它们的类型各不相同,但是共享同一种调用形式:

int(int,int)

我们可能希望使用这些可调用对象构建一个简单的桌面计算器。

为了实现这一目的,需要定义一个函数表(function table)用于存储指向这些可调用对象的“指针”。当程序需要执行某个特定的操作时,从表中查找该调用的函数。

在C++语言中,函数表很容易通过map 来实现。

对于此例来说,我们使用一个表示运算符符号的string对象作为关键字;使用实现运算符的函数作为值。当我们需要求给定运算符的值时,先通过运算符索引map,然后调用找到的那个元素。

假定我们的所有函数都相互独立,并且只处理关于int 的二元运算,则map可以定义成如下的形式:

// 构建从运算符到函数指针的映射关系,其中函数接受两个int、返回一个int
map<string, int(*)(int,int)> binops;

我们可以按照下面的形式将ada的指针添加到binops中. 

//正确:add是一个指向正确类型函数的指针
binops.insert(("+", add));
//("+",add}是一个pair

 但是我们不能将mod或者divide存入binops:

binops.insert(("%",mod));
//错误:mod不是一个函数指针

问题在于mod是个lambda表达式,而每个lambda 有它自己的类类型,该类型与存储在binops中的值的类型不匹配。

标准库function类型

我们可以使用一个名为function 的新的标准库类型解决上述问题,function定义在functional头文件中,表14.3列举出了function定义的操作。

function的操作
functioncT> ff是一个用来存储可调用对象的空function,这些可调用对象的调用形式应该与函数类型T相同(即T是retType(args))
function<T> f(nullptr);显式地构造一个空function
function<T> f(obj);在f中存储可调用对象。obj的副本
f将f作为条件:当含有一个可调用对象时为真;否则为假
f (args)调用f中的对象,参数是args
定义为function<T>的成员的类型
result_type该 function类型的可调用对象返回的类型
argument _type
first_argument_type
second argument _type
当T有一个或两个实参时定义的类型。如果只有一个实参,则argument_type 是该类型的同义词;如果有两个实参,则 first_argument_type 和 second argument_type分别代表两个实参的类型

function 是一个模板,和我们使用过的其他模板一样,当创建一个具体的function 类型时我们必须提供额外的信息。

在此例中,所谓额外的信息是指该function 类型能够表示的对象的调用形式。参考其他模板,我们在一对尖括号内指定类型:

function<int(int, int)>

在这里我们声明了一个function类型,它可以表示接受两个int、返回一个int的可调用对象。

因此,我们可以用这个新声明的类型表示任意一种桌面计算器用到的类型;

function<int(int, int)> f1 = add;//函数指针
function<int(int, int)> f2 = divide();//函数对象类的对象
function<int(int, int)> f3=[](int i, int j){cout << f1(4,2) << endl; return i*j;};
//lambda

cout << f1(4,2)<<endl;//打印6
cout << f2(4,2)<< endl;//打印2
cout << f3(4,2) << endl;//打印8

使用这个 function类型我们可以重新定义map:

//列举了可调用对象与二元运算符对应关系的表格
//所有可调用对象都必须接受两个int、返回一个int
// 其中的元素可以是函数指针、函数对象或者 lambda 
map<string, function<int(int, int)>> binops;

我们能把所有可调用对象,包括函数指针、lambda或者函数对象在内,都添加到这个map


map<string, function<int(int,int)>>binops={
{"+",add},                           // 函数指针
{"-",std::minus<int>()},                 // 标准库函数对象    
{"/",divide()},                        //用户定义的函数对象
{"*",[](int i,int j){return i*j;}},//未命名的lambda
{"%",mod} };                        //命名了的lambda对象

我们的map中包含5个元素,尽管其中的可调用对象的类型各不相同,我们仍然能够把所有这些类型都存储在同一个function<int (int,int)>类型中。

一如往常,当我们索引map时将得到关联值的一个引用。如果我们索引binops,将得到function对象的引用。

function类型重载了调用运算符,该运算符接受它自己的实参然后将其传递给存好的可调用对象:

binops["+"](10,5);//调用add(10,5)
binops["-"](10,5);// 使用minus<int>对象的调用运算符
binops["/"](10,5);// 使用divide对象的调用运算符
binops["*"](10,5);// 调用lambda 函数对象
binops["%"](10,5);//调用lambda函数对象

我们依次调用了binops中存储的每个操作。在第一个调用中,我们获得的元素存放着一个指向add函数的指针,因此调用binops["+"](10,5)实际上是使用该指针调用add,并传入10和5。

在接下来的调用中,binops["-"]返回一个存放着std::minus<int>类型对象的function,我们将执行该对象的调用运算符。

重载的函数与function

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

int add(int i, int j) { return i + j; )
Sales_data add(const Sales _data&,const Sales_data&);

map<string, function<int(int, int)>> binops;

binops.insert(("+",add});
// 错误:哪个 add?

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

int (*fp)(int,int) = add;//指针所指的add是接受两个int的版本

binops.insert(("+",fp));// 正确:fp指向一个正确的add版本

同样,我们也能使用lambda 来消除二义性: 

// 正确:使用 lambda 来指定我们希望使用的add版本
binops.insert( ("+",[](int a, int b) (return add(a, b)i)));

lambda 内部的函数调用传入了两个int,因此该调用只能匹配接受两个int的add版本,而这也正是执行lambda时真正调用的函数。
 

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

内核中常用宏定义| container_of

文章目录 前言container_of函数介绍container_of函数实现container_of函数解析offsetof的使用container_of的使用结语 前言 前两篇我们写到内核中两种C语言高级语法__attribute__, __read_mostly。本篇写内核中另外一种常用宏定义之container_of container_of函数介绍 conta…

高级事件.

高级事件 1. 注册事件&#xff08;addEventListener)2.删除事件(removeEventListener&#xff09;3.DOM事件流4.事件对象及其方法&#xff08;当形参来看&#xff09;5.阻止默认事件/冒泡6.事件委托7.鼠标事件&#xff08;禁止右键/选中文字)8.鼠标事件对象8.常用键盘事件9.键盘…

【C++】模板初阶:泛型编程的起点

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

大模型下的Agent、AIGC的商业案例集合

算是一份摘录 1 AIGC 对影楼的影响 https://mp.weixin.qq.com/s/3j-6FAxZEEvXUZ1q6by2uw 2 出海Talkie &#xff1a;情感智能体 https://mp.weixin.qq.com/s/KHPmfuVvywxxcI2rqoOghA Talkie 为每条消息提供 3 个免费灵感&#xff0c;如果用户需要更多 AI 生成的灵感选项&…

Delta lake with Java--在spark集群上运行程序

昨天写了第一篇入门&#xff0c;今天看见有人收藏&#xff0c;继续努力学习下去。今天要实现的内容是如何将昨天的HelloDetlaLake 在spark集群上运行&#xff0c;。具体步骤如下 1、安装spark,我使用的是 spark-3.5.1-bin-hadoop3-scala2.13&#xff0c;去官网下载&#xff0c…

无穷级数错题本

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <

2024五一赛数学建模A题B题C题完整思路+数据代码+参考论文

A题 钢板最优切割路径问题 &#xff08;完整资料在文末获取&#xff09; 1. 建立坐标系和表示方法&#xff1a; 在建模之前&#xff0c;我们需要将切割布局转换为数学表示。首先&#xff0c;我们可以将布局中的每个点表示为二维坐标系中的一个点。例如&#xff0c;B1可以表示…

Ubuntu服务器创建新用户及解决新用户登录Access denied问题

目录 Ubuntu服务器创建新用户及解决新用户登录Access denied问题创建账号步骤创建用户只创建用户添加用户到sudo组 允许账号远程连接重启ssh服务 删除账号要删除用户而不删除用户文件如果要删除并且删除用户的家目录和邮件 查询指令查看所有用户查询特定用户账户信息查看用户组…

【Java基础】Maven的生命周期(clean+site+default)

1. 前言 在 Maven 出现之前&#xff0c;项目构建的生命周期就已经存在&#xff0c;开发人员每天都在对项目进行清理&#xff0c;编译&#xff0c;测试及部署&#xff0c;但由于没有统一的规范&#xff0c;不同公司甚至不同项目之间的构建的方式都不尽相同。 Maven 从大量项目…

[C++基础学习-07]----C++结构体详解

前言 结构体&#xff08;Struct&#xff09;是C中一种用户定义的复合数据类型&#xff0c;用于存储不同类型的数据项。结构体可以包含不同类型的数据成员&#xff0c;这些数据成员可以是基本类型&#xff08;如int、float、char等&#xff09;&#xff0c;也可以是数组、指针、…

Linux编辑器——vim的基础使用

文章目录 1.vim的基本概念2.vim的基本操作3.vim命令模式命令集3.1移动光标3.2删除文字3.3复制3.4替换3.5撤销3.6更改3.7跳到指定的行 1.vim的基本概念 本文将介绍vim的三种模式&#xff0c;分别位&#xff1a;命令模式、插入模式、低行模式。他们的功能区分如下&#xff1a; 正…

2. 深度学习笔记--损失函数

在机器学习中&#xff0c;损失函数是代价函数的一部分&#xff0c;而代价函数则是目标函数的一种类型。 Loss function&#xff0c;即损失函数&#xff1a;用于定义单个训练样本与真实值之间的误差&#xff1b; Cost function&#xff0c;即代价函数&#xff1a;用于定义单个批…

学习和“劳动”相关的谚语,柯桥俄语培训

1. Бог труды́ лю́бит. 天道酬勤。 2. В ми́ре нет тру́дных дел, ну́жно лишь усе́рдие. 世上无难事,只怕有心人。 3. У́тро вечера мудренее. 一日之计在于晨。 4. Что посе́ешь,…

车载电子电器架构 —— 关于bus off汇总

车载电子电器架构 —— 关于bus off汇总 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明…

[Java EE] 多线程(六):线程池与定时器

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (90平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;Java …

语义分割——铁路轨道数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 重…

NASA数据集——NOAA 气溶胶和海洋科学考察数据(AEROSE)

Saharan Dust AERosols and Ocean Science Expeditions 简介 NOAA 气溶胶和海洋科学考察&#xff08;AEROSE&#xff09;是一种基于测量的综合方法&#xff0c;用于了解热带海洋上空气溶胶长程飘移的影响&#xff08;Morris 等人&#xff0c;2006 年&#xff1b;Nalli 等人&a…

直流屏整流模块HG07A220R电源模块HG10A220R

直流屏整流模块HG07A220R电源模块HG10A220R 其他同类型监控模块PM09T电源模块HG22005/S&#xff0c;HG22010/S&#xff0c;HG11010/S&#xff0c;HG11020/S&#xff0c;HG10A220Z&#xff0c;HG10A220F&#xff0c;HG05A220Z&#xff0c;HG07A220Z&#xff0c;HG10A110Z&#x…

Electron 对 SQLite 进行加密

上一篇讲了如何在 Electron使用 SQLite&#xff0c;如果 SQLite 中存有敏感数据&#xff0c;客户端采用明文存储风险很高&#xff0c;为了保护客户数据&#xff0c;就需要对数据进行加密&#xff0c;由于 electron 对代码并不加密&#xff0c;所以这里排除通过逆向工程进行数据…

从论文中看AI绘画

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 主要看是看Diffusion Models,CLIP,ControlNet,IP-Adapter这种经典论文,尝试总结论文写作的一些方式以及图像生成模型的一些内在思想. 对于其中的数学原理和代码不过深究. DDPM 使用扩散模型得到高质量图像,证明了这…