C++11 function包装器

news2025/4/3 14:02:08

前言

在C++中,有三种可调用对象:函数指针,仿函数,lambda表达式。
三者有相似的作用和效果,但使用形式有很大的差异。
为了进行统一,C++11引进了function包装器

在这里插入图片描述

文章目录

  • 前言
  • 一. function的使用
  • 二. function对成员函数的包装
  • 三. bind绑定
    • 1. 调整参数顺序
    • 2. 调整参数个数
  • 结束语

一. function的使用

首先,要想使用function,需要包含functional这个头文件

在这里插入图片描述

function包装器本质是一个类模板

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;

模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

以往,如果要实现一个加法,函数指针,仿函数,lambda表达式的实现方式如下:

#include<iostream>
#include<functional>

using namespace std;

//函数
int func(int a, int b)
{
	cout << "int func(int a, int b)" << endl;
	return a + b;
}
//仿函数
class Func
{
public:
	int operator()(int a, int b)
	{
		cout << "int operator()(int a, int b)" << endl;
		return a + b;
	}
};


int main()
{
	//函数指针
	int(*fp)(int, int) = func;
	fp(1, 3);
	
	//仿函数
	Func ff;
	ff(1, 3);
	
	//lambda表达式
	auto fl=[](int a,int b)->int
	{
		cout << "[](int a,int b)->int{}" << endl;
		return a + b;
	};
	fl(1, 3);
	
	return 0;
}

虽然都可以完成需求,但使用方式差别太大,而function就可以将三者统一包装起来

在这里插入图片描述

可以看到,function包装器统一封装了三种可调用对象,且最终类型相同,这就是包装器的使用
< >中,第一个int是返回值类型,( )中是参数类型

functiona的使用还有如下场景:

map<string, function<int(int, int)>>funcMap;
funcMap["函数指针"] = fp;
funcMap["仿函数"] = Func();
funcMap["lambda"] = [](int a, int b)->int
{
	cout << "[](int a,int b)->int{}" << endl;
	return a + b;
};

二. function对成员函数的包装

除了以上的三种可调用对象,成员函数同样可以使用function包装,不过使用有些不同

首先,对于静态成员函数,function的包装没有什么差别

class Func
{
public:
	//静态成员函数
	int static staFunc(int a)
	{
		cout << "int static staFunc(int a)" << endl;
		return a;
	}
	//普通成员函数
	int norFunc(int a)
	{
		cout << "int norFunc(int a)" << endl;
		return a;
	}
};

int main()
{
	//静态成员函数
	//function<int(int)>f1 = Func::staFunc;//可以不需要&
	function<int(int)>f1 = &Func::staFunc;//需要指类域
	
	return 0;
}

对于成员函数,包装器要求&,不过静态成员函数可以不使用&
但是对于普通成员函数,即使加了&,也不行

在这里插入图片描述

因为普通的成员函数其实有一个隐藏的参数——this指针
所以如果要包装普通成员函数,function的类模板还需要加上类对象

//普通成员函数
function<int(Func,int)>f2 = &Func::norFunc;
f2(Func(),10);//使用

PS:类对象不是对应this指针,而是function调用成员函数时,需要通过类对象调用成员函数
其实也可以传Func*,function就是通过类对象指针调用成员函数
但是这样就无法使用匿名对象,因为匿名对象是右值,不能取地址

function<int(Func*,int)>f2 = &Func::norFunc;
f2(&Func(),10);//错误

三. bind绑定

functional头文件中,还有一个比较实用的函数适配器——bind

bind是一个函数模板,接收一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表
一般而言,我们可以使用bind把一个原本需要传N个参数的函数fn,通过绑定一些参数,返回一个需要传M个(M可以大于N,但这么做没什么意义)参数的新函数。
同时,使用bind函数还可以实现参数顺序的调整等操作

在这里插入图片描述

1. 调整参数顺序

通过bind绑定可以调整参数顺序
比如:

void Func(int a, int b)
{
	cout << a << " ";
	cout << b << endl;
}

如果我们要调整参数a和b的顺序

bind的使用如下:

int main()
{
	function<void(int, int)>f1 = Func;
	f1(10, 20);

	auto f2 = bind(f1, placeholders::_2, placeholders::_1);
	f2(10, 20);

	return 0;
}

placeholders是头文件functional中的一个命名空间

在这里插入图片描述

placeholders::_1和placeholders::_2都是占位符
function本质是仿函数,而bind是根据占位符,和提供的可调用对象重新生成仿函数。
因为bind是函数模板,参数是万能引用,所以函数指针,仿函数,lambda同样可以调整

auto f3 = bind(Func, placeholders::_2, placeholders::_1);

返回的是一个_Binder类
在这里插入图片描述
内部包装了可调用对象,并且根据提供的占位符调整参数顺序

2. 调整参数个数

在使用function包装成员函数时,因为有this指针的存在,使得代码编写较为复杂,而bind则可以提前绑死参数

使用如下:

class Func
{
public:
	int norFunc(int a)
	{
		cout << "int norFunc(int a)" << endl;
		return a;
	}
};

int main()
{
	function<int(Func,int)>f1 = &Func::norFunc;
	f1(Func(), 5);
	//使用bind提前绑定Func对象
	auto f2 = bind(&Func::norFunc, Func(), placeholders::_1);
	f2(5);
}

提前绑死Func对象,就不需要再调用时,显式传Func对象

结束语

感谢你的阅读

如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

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

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

相关文章

DataGrip连接clickhouse数据库后,左侧不显示库中的表

一、问题描述 使用datagrip链接clickhouse&#xff0c;本来左侧时可以显示数据库中对应的表的列表的。但是&#xff0c;这个链接clickhouse不显示。 二、问题解决 参考一些文档后&#xff0c;而且因为当前采用的时2023.1.2的版本&#xff0c;所以&#xff0c;选项有些改变。…

记录AndroidStrudio打包报错与解决方案(一)

问题一&#xff1a;打包报错 Unity.IL2CPP.Building.BuilderFailedException: C:\Users\user\AppData\Local\Android\Sdk\ndk\21.0.6113669\toolchains\llvm\prebuilt\windows-x86_64\bin\clang "C:\Users\user\AppData\Local\Temp\tmp7277.tmp" -o "D:\Buil…

Linux计划任务crontab

顺序是可以任意的, 任意指的是,选项可以都放前面 变量放后面 或者一个选项 一个选项对应的命令 df 查看文件占有情况 d1 只显示1级深度 -h格式好看点 显示所有用户进程和内存进程. uid是所属用户 PPID是父进程 kill终止进程 杀死进程&#xff0c;是正常退出。 如果普通杀不…

数据智能交融,AI引领未来 | 数说故事成为华为云盘古大模型3.0首批联创单位之一

7月7日-9日&#xff0c;华为开发者大会2023&#xff08;Cloud&#xff09;在东莞举行&#xff0c;并在7日下午正式对外发布“华为云盘古大模型3.0”。盘古大模型3.0围绕行业重塑、技术扎根、开放同飞三大方向&#xff0c;持续打造核心竞争力&#xff0c;为行业客户、伙伴及开发…

疯狂收割offer,软件测试面试题,项目经验板块常问(附答案)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 面试题&#xff1…

CCLINK转MODBUS-TCP网关cclink利modbus区别

大家好&#xff0c;今天我们要聊的是生产管理系统中的CCLINK和MODBUS-TCP协议&#xff0c;它们的不同使得数据互通比较困难&#xff0c;但远创智控YC-CCLK-TCP网关的出现改变了这一切。 1&#xff0c; 远创智控YC-CCLK-TCP是一款自主研发的CCLINK从站功能的通讯网关&#xff…

快讯丨北京市委书记尹力到极智嘉调研指导

据北京日报7月4日报道&#xff0c;2023全球数字经济大会开幕前夕&#xff0c;市委书记尹力围绕“深入学习贯彻新时代中国特色社会主义思想&#xff0c;推进全球数字经济标杆城市建设”&#xff0c;到海淀区、朝阳区调查研究。 7月3日下午&#xff0c;北京市委书记尹力到极智嘉全…

一分钟让你学会文件的打开与关闭函数

文章目录 前言文件打开函数-fopen函数介绍文件的打开形式相对路径与绝对路径 文件关闭函数-fclose文件操作正确流程 前言 我们已经了解了文件基本概念&#xff0c;那么我们如何通过代码对某一个文件进行一些文件操作呢&#xff1f;比如如何打开文件、关闭文件以及如何向文件中…

promQL详细语法介绍

目录 promQL 在表达式中支持的数据类型 简单语法介绍 rate的用法 指标过滤搜索 时间单位 聚合表达式 11个聚合函数 二元运算符&#xff08;Binary Operators&#xff09; 二元运算符优先级 向量匹配 向量一对一匹配 向量一对多/多对一匹配 promQL 在表达式中支持的数据…

抽象类与纯虚函数的具体应用

运行代码&#xff1a; //定义一个带有一个纯虚函数pvf()的B2类 //定义D21类&#xff0c;包含一个string数据成员和一个覆盖pvf()的成员函数&#xff0c;D21::pfv()输出string数据成员的值 //定义D22类&#xff0c;它与D21类一样&#xff0c;只是数据成员为int类型 //定义函数f…

数据库应用:MySQL数据库SQL高级语句与操作

目录 一、理论 1.克隆表与清空表 2.SQL高级语句 3.SQL函数 4.SQL高级操作 二、实验 1.克隆表与清空表 2.SQL高级语句 3.SQL函数 4.SQL高级操作 三、总结 一、理论 1.克隆表与清空表 克隆表&#xff1a;将数据表的数据记录生成到新的表中。 &#xff08;1&#xff0…

【算法集训之线性表篇】Day 04

文章目录 题目一分析思路一思路二 代码实现效果题目二分析代码实现效果 题目一 从有序顺序表中删除所有其值重复的元素&#xff0c;使所有元素的值都不相同。 分析 思路一 首先&#xff0c;在有序顺序表中&#xff0c;所有值重复的元素相邻。为此&#xff0c;我们只需顺序访…

如何批量复制淘宝商品上传上架到虾皮 shopee (轻松学会宝贝复制技巧)

今天&#xff0c;入驻虾皮的商家越来越多&#xff0c;且很多的 shopee 店主在国内都开了淘宝店&#xff0c;当 shopee 店铺入驻成功后&#xff0c;想把淘宝店铺的商品搬到 shopee&#xff0c;怎么搬呐&#xff1f; 方法 / 步骤 方法1&#xff1a;整理你想要采集的店铺首页链接…

MySQl数据库第六课-------SQl命令的延续------快来看看

作者前言 欢迎小可爱们前来借鉴我的gtiee秦老大大 (qin-laoda) - Gitee.com ———————————————————————————————— 目录 SQl语句 数据库操作 数据表操作 SQL增删 ———————————————————————————— 插播小知识 1…

stm32(独立看门狗和窗口看门狗)

独立看门狗介绍 什么是看门狗&#xff1f; 在由单片机构成的微型计算机系统中&#xff0c;由于单片机的工作常常会受到来自外界电磁场的干扰&#xff0c;造 成程序的跑飞&#xff0c;而陷入死循环&#xff0c;程序的正常运行被打断&#xff0c;由单片机控制的系统无法继续工作…

setTimeout

Promise延时的几种表述方法 第一种 var pdocument.getElementById("demo"); async function myFunction(){await new Promise(function(resolve){setTimeout(function(){p.innerHTML"延时成功";resolve();},1000);}); } myFunction();第二种 var pdocum…

完了完了完了!线上发生 OOM 了!

现象 线上某个服务有接口非常慢&#xff0c;通过监控链路查看发现&#xff0c;中间的 GAP 时间非常大&#xff0c;实际接口并没有消耗很多时间&#xff0c;并且在那段时间里有很多这样的请求。 原因分析 先从监控链路分析了一波&#xff0c;发现请求是已经打到服务上了&#…

ViperGPT解析:结合视觉输入与文本查询生成和执行程序

ViperGPT&#xff1a;结合视觉输入与文本查询生成和执行程序 ViperGPT 是一个混合视觉和语言处理模型&#xff0c;旨在解决视觉查询问题。这种问题需要视觉处理和推理能力的结合&#xff0c;ViperGPT通过利用代码生成模型&#xff0c;将视觉和语言模型组合成子例程&#xff0c…

【Java】弄清多态,看这一篇就够了|由浅入深,保姆级详解

博主简介&#xff1a;努力学习的预备程序媛一枚~博主主页&#xff1a; 是瑶瑶子啦所属专栏: Java岛冒险记【从小白到大佬之路】 前言 在上篇【Java】还不理解继承&#xff1f;一篇文章看懂继承|继承入门&#xff0c;我们了解了继承的概念、如何时两个类建立继承关系is-a、以及…

AndroidUI绘制流程

Android源码阅读 UI绘制流程 环境 Java 11android 11 由于学习的课程api 不一致 导致源码有些关键方法无法进入仔细阅读 采用截图的方式理解思路 view添加到窗口 进入到源码中可以发现 &#xff0c;每个activity 默认生成的代码中都会有一个setContentView方法&#xff0c…