function包装器和bind包装器

function包装器和bind包装器

  • 包装器
  • function包装器
    • 为什么需要function
    • function包装器
    • function包装器的应用场景
      • 逆波兰表达式求值
  • bind包装器
    • bind包装器的应用场景

包装器

包装器是用于给其他编程接口提供更一致或更合适的接口

由于函数调用可以使用函数名、函数指针、函数对象和lambda表达式,可调用类型太丰富导致模板的效率极低。包装器用于解决效率低的问题

function包装器

function是一种函数包装器,也叫做适配器。它可以对可调用对象进行包装,C++中的function本质就是一个类模板

为什么需要function

我们看如下例子:

template<class F, class T>
T useF(F f, T x)
{
	static int count = 0;
	cout << "count:" << ++count << endl;
	cout << "count:" << &count << endl;

	return f(x);
}

double f(double i)
{
	return i / 2;
}

//仿函数
struct Functor
{
	double operator()(double d)
	{
		return d / 3;
	}
};

void test1()
{
	// 函数名
	cout << useF(f, 11.11) << endl;
	// 仿函数
	cout << useF(Functor(), 11.11) << endl;
	// lamber表达式
	cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;
}

useF中的f(x),f可能是什么呢?函数名?函数指针?仿函数对象?lambda表达式对象?这些都是可调用的类型对象,这就会导致模板的效率低下。怎么个低下法呢?我们执行上述代码,得到结果:在这里插入图片描述

可以看到,上述代码产生了三个不同的静态变量count,说明useF函数模板被实例化出了三份。之所以被实例化出三份,是因为fFunctor()[](double d)->double { return d / 4; }的类型并不相同。

那么为了提高效率,能不能让useF函数模板只实例化一份呢?换句话说,能不能让这三个类型都统一成一个类型?–function包装器

function包装器

std::function本质是个类模板,在头文件<functional>

template <class Ret, class... Args>
class function<Ret(Args...)>;		//Args...是参数列表
  • Ret: 被调用函数的返回类型
  • Args…:被调用函数的形参

根据上面的例子,被调用的函数是double f(double i),因此Ret和Args都是double

所以void test1()中对于useF的调用可以写成这样:

void test1()
{
	// 函数名
	function<double(double)> f1 = f;
	// 仿函数对象
	function<double(double)> f2 = Functor();
	// lamber表达式对象
	function<double(double)> f3 = [](double d)->double { return d /4; };

	cout << useF(f1, 11.11) << endl;
	cout << useF(f2, 11.11) << endl;
	cout << useF(f3, 11.11) << endl;
}

运行结果如下:在这里插入图片描述

可以看到只生成了一个静态变量count,说明只实例化出了一份函数。这样就提高了效率

function包装器的应用场景

  • 场景一:上面所讲的提高模板的使用效率
  • 场景二:将可调用对象的类型统一

针对场景二,进行详细讲解:

为什么要将这些可调用对象的类型进行统一呢?因为统一后,可以将不同的可调用对象都存储在同一个容器中,方便管理

以前想把那些可调用对象存在同一个vector中是几乎不可能的,现在借助包装器,将可调用对象类型统一后,就可以存储了

比如,延续上面的例子:

	// 函数名
	function<double(double)> f1 = f;
	// 仿函数对象
	function<double(double)> f2 = Functor();
	// lamber表达式对象
	function<double(double)> f3 = [](double d)->double { return d / 4; };

	//写法一:
	vector<function<double(double)>> v = { f1,f2,f3 };

	//写法二:直接写:
	vector<function<double(double)>> v = { f,Functor(),[](double d)->double { return d / 4; } };

下面给出一个实例:

逆波兰表达式求值

150. 逆波兰表达式求值 - 力扣(LeetCode)

在这里插入图片描述

在这里插入图片描述

  • 以前的做法:
    int evalRPN(vector<string>& tokens) 
    {
        stack<int> st;
        //将tokens中的数字依次入栈,若遇到操作符则出栈计算
        for(auto& str:tokens)
        {
            if(str=="+" || str=="-" || str=="*" || str=="/")
            {
                int right=st.top();
                st.pop();
                int left=st.top();
                st.pop();

                switch(str[0])
                {
                    case '+':
                        st.push(left+right);
                        break;
                    case '-':
                        st.push(left-right);
                        break;
                    case '*':  
                        st.push(left*right);
                        break;
                    case '/':
                        st.push(left/right);
                        break;
                }
            }
            else
            {
                st.push(stoi(str));//将字符串变成整型存入栈中
            }
        }

        return st.top();
    }
  • 现在利用function包装器实现:

这里是一个命令对应一个动作,比如遇到”+“,就实现两个数相加。

(凡是类似这种场景,都可以利用包装器。就比如说linux的命令,就可以这样实现)

    int evalRPN(vector<string>& tokens) 
    {
        map<string,function<int(int,int)>> m ={
            {"+",[](int x,int y){return x+y;}}
            ,{"-",[](int x,int y){return x-y;}}
            ,{"*",[](int x,int y){return x*y;}}
            ,{"/",[](int x,int y){return x/y;}}
        };

        stack<int> st;
        //将tokens中的数字依次入栈,若遇到操作符则出栈计算
        for(auto& str:tokens)
        {
            if(m.count(str))
            {
                int right=st.top();
                st.pop();
                int left=st.top();
                st.pop();
                st.push(m[str](left,right));
            }
            else
            {
                st.push(stoi(str));//将字符串变成整型存入栈中
            }
        }

        return st.top();
    }

上述代码还有一个优势,就是如果再增加运算符操作的话,只需在map这增加代码即可,其他地方不需要改动

这里为了简便就用了lambda表达式。当然也可以写函数名、仿函数对象,如下:

class Solution 
{
public:
    static int add(int x, int y)
    {
        return x+y;
    }

    struct sub
    {
        int operator()(int x,int y)
        {
            return x-y;
        }
    };

    int evalRPN(vector<string>& tokens) 
    {
        map<string,function<int(int,int)>> m ={
            {"+",add}
            ,{"-",sub()}
            ,{"*",[](int x,int y){return x*y;}}
            ,{"/",[](int x,int y){return x/y;}}
        };

	//……
    }
};

int add(int x, int y)前面要加一个static是因为:这个函数是在类中,类成员函数一般都是由对象去调用的,这里只想要用它的地址 加上静态关系后,就不需要依靠对象调用了,光写函数名就能代表函数的地址了

bind包装器

bind也是一种函数包装器,也叫做适配器。它可以接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。 C++中的bind本质是一个函数模板,也在头文件<functional>

bind包装器的应用场景

  1. 调整函数的参数顺序

我们看如下代码:

int Sub(int x, int y)
{
	return x - y;
}

int main()
{
	function<int(int, int)> rSub1 = bind(Sub, placeholders::_1, placeholders::_2);
	cout << rSub1(10, 5) << endl;

	function<int(int, int)> rSub2 = bind(Sub, placeholders::_2, placeholders::_1);
	cout << rSub2(10, 5) << endl;
	return 0;
}

这里的placeholders是个命名空间,_1_2都是命名空间中的标识符

运行结果:在这里插入图片描述

我们看改变的地方只有一处:在这里插入图片描述

这就是利用bind实现的参数顺序的调换

我们理一下调用逻辑:

  • 对于rSub1:

    在这里插入图片描述

  • 对于rSub2:

    在这里插入图片描述

总结:

  • bind过后的函数传参时:第一个实参只会传给标识符_1,第二个实参只会传给标识符_2,以此类推

    这里传几个实参,bind语句中就有几个_,且是对应死的。第一个实参就对应于_1

    所以说,在上面写个_3是会报错的

  • bind传给原函数时:不再看什么标识符,按照位置关系进行传参

  1. 调整函数的传参个数(也就是将原本函数中的某一个参数绑定死)

看如下代码:

double Plus(int x, int y, double rate)
{
	return (x + y) * rate;
}

int main()
{
	function<double(int, int)> plus1 = bind(Plus, placeholders::_1, placeholders::_2, 4.1);
	function<double(int, int)> plus2 = bind(Plus, placeholders::_1, placeholders::_2, 4.3);
	function<double(int, int)> plus3 = bind(Plus, placeholders::_1, placeholders::_2, 4.5);

	cout << plus1(5, 3) << endl;
	cout << plus2(5, 3) << endl;
	cout << plus3(5, 3) << endl;

	return 0;
}

这里就是将Plus函数的第三个参数rate固定

尽管缺省参数也可以实现将参数固定,但缺省参数只能固定一种情况,bind可以固定任意情况

此外,还要注意在写类型时,被绑定的参数的类型就不需要写在function中了

假如我最初函数写成这样:

在这里插入图片描述

那么bind语句就这样写:

在这里插入图片描述

注意这里还是_1和_2。因为这个是和bind后的函数的第几个实参对应的。

  1. 绑定类的成员函数

看如下代码:

class SubType
{
public:
	int sub(int x, int y)
	{
		return x - y;
	}

	static  int ssub(int a, int b, int rate)
	{
		return (a - b) * rate;
	}
};

int main()
{
	//bind静态成员函数
	function<int(int, int)> rssub = bind(SubType::ssub, placeholders::_1, placeholders::_2,3);
	cout << rssub(5, 3) << endl;

	//bind成员函数
	function<int(int, int)> rsub = bind(&SubType::sub,SubType(), placeholders::_1, placeholders::_2);
	cout << rsub(5, 3) << endl;

	return 0;
}

注意点:

  • 绑定类成员函数时,一定要声明这个函数在哪个类中,如上SubType::
  • bind成员函数时,需要在函数名前面加上&(静态成员函数可加可不加)
  • bind成员函数时,需要增加一个参数:类对象或者类对象的指针

其实,绑定的底层也是仿函数

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

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

相关文章

MSPM0G3507——PWM

在sysconfig中&#xff0c;左侧可以选择MCU的外设&#xff0c;我们找到并点击TIMER-PWM选项卡&#xff0c;在TIMER-PWM中点击ADD&#xff0c;就可以添加定时器下的PWM外设。 这里设置通道0为100Hz的频率&#xff0c;0%占空比的PWM&#xff0c;周期计数值为1000&#xff0c;比较…

Linux中的文本编辑器vi与vim

摘要&#xff1a; 本文将深入探讨VI和VIM编辑器的基本概念、特点、使用方法以及它们在Linux环境中的重要性。通过对这两款强大的文本编辑器的详细分析&#xff0c;读者将能够更全面地理解它们的功能&#xff0c;并掌握如何有效地使用它们进行日常的文本编辑和处理任务。 引言&…

标准立项 | 《温室气体排放核算与报告要求 废油资源化企业》

《温室气体排放核算与报告要求 废油资源化企业》适用于废油资源化行业企业温室气体排放量的核算和报告。从事废油资源化生产的企业&#xff0c;均可参考该标准核算企业的温室气体排放量&#xff0c;并编制企业温室气体排放报告。 参编咨询&#xff1a;中华环保联合会水环境治理…

新火种AI|Claude 3.5一夜封王超越GPT-4o!留给OpenAI的时间真的不多了...

AI大模型更新换代的速度&#xff0c;的确快到令人难以想象。 相信很多人现在对“最先进AI大模型”的印象还停留在GPT-4&#xff0c;但事实上&#xff0c;大模型领域的头把交椅早已悄然易主了好几回。就在GPT-4惊艳全球不久之后&#xff0c;其“死对头” Anthropic发布了Claude…

2024/6/22 英语每日一段

France is the only country in Europe with an EPR that covers the textile industry. Critics say the policy does little for “end-of-line” countries such as Ghana because the fee paid by clothing producers is low at just €0.06 for each item, and the funds …

8_机械臂工作台坐标系标定及验证

1、机械臂实际数据 AUBO 机械臂xOxy方式标定用户坐标系&#xff1a; O: X轴正半轴一点&#xff1a; XOY象限任意一点(还是有一些要求的): 一些坐标点的验证&#xff1a; 2、如何根据上述3点&#xff0c;计算work1坐标系与base坐标系的关系&#xff1f; 最开始在网上没找到相关的…

90V转12V1A恒压WT6039

90V转12V1A恒压WT6039 WT6039降压DC-DC转换器芯片专为处理宽泛的电压输入范围设计&#xff0c;支持从12V至90V。该芯片集成了关键功能&#xff0c;如使能控制开关、参考电源、误差放大器、过热保护、限流保护及短路保护等&#xff0c;以确保系统在各种操作条件下的安全与稳定性…

【朝花夕拾】RT1170 CSI 如何使能摄像头Y8功能

【朝花夕拾】RT1170 CSI 如何使能摄像头Y8功能 一&#xff0c;文档简介二&#xff0c;RT1170 CSI Y8黑白格式配置与测试2.1 软硬件情况2.2 Y8黑白格式的具体配置2.3 测试结果 一&#xff0c;文档简介 RT1170的CSI可以支持YUV格式&#xff0c;所谓的YUV分为三个分量&#xff1a…

xocde编辑器支持修改为中文吗?不支持

xocde编辑器支持修改为中文吗&#xff1f; 不支持

储能电池竞争出海分析

锂电池的激烈竞争进一步蔓延到储能行业。为保市场份额和现金流稳定&#xff0c;不少储能电池企业都开始大幅度降低报价只求中标储能项目。 随着6月的储能电芯的最高限价和系统报价都已经贴近成本价&#xff0c;一二三线的储能电池厂商将要如何应对&#xff1f; 1、储能规模快速…

Redis进阶 - Redis 淘汰策略

我们知道Redis是分布式内存数据库&#xff0c;基于内存运行&#xff0c;可是有没有想过比较好的服务器内存也不过几百G&#xff0c;能存多少数据呢&#xff0c;当内存占用满了之后该怎么办呢&#xff1f;Redis的内存是否可以设置限制&#xff1f; 过期的key是怎么从内存中删除的…

重学java 80.Junit单元测试

我总是着急的解释我自己&#xff0c;却忘了厚爱无需多言 —— 24.6.21 一、Junit介绍 1.概述 Junit是一个单元测试框架,可以代替main方法去执行其他的方法 2.作用 可以单独执行一个方法,测试该方法是否能跑通 3.注意 Junit是第三方工具,所以使用之前需要导入jar包 二、J…

1.SG90

目录 一.实物图 二.原理图 三.简介 四.工作原理 一.实物图 二.原理图 三.简介 舵机&#xff08;英文叫Servo&#xff09;&#xff0c;是伺服电机的一种&#xff0c;伺服电机就是带有反馈环节的电机&#xff0c;这种电机可以进行精确的位置控制或者输出较高的扭矩。舵机…

在线二维码解码器:将二维码转换成网址链接

在当今数字化时代&#xff0c;二维码&#xff08;QR码&#xff09;已成为一种便捷的信息传递工具。它不仅可以存储大量数据&#xff0c;还能快速分享信息。然而&#xff0c;有时我们需要将二维码中的内容转换为网址链接&#xff0c;以便在浏览器中直接访问。小编将详细介绍如何…

关于jupyter notebook的使用经验

jupyter notebook 第一点&#xff0c;调整每次打开jupyter notebook的时候的位置第二点&#xff0c;如何设置jupyter notebook可以使用本地anaconda创建的虚拟环境呢&#xff1f;第三点&#xff0c;使用jupyter notebook的技巧 以下三点都是独立的&#xff0c;可以根据自己的需…

【Gradio】构建自定义多模态聊天机器人

这是我们构建自定义多模态聊天机器人组件两部分系列的第一部分。在第一部分中&#xff0c;我们将修改 Gradio 聊天机器人组件&#xff0c;使其能够在同一消息中显示文本和媒体文件&#xff08;视频、音频、图片&#xff09;。在第二部分中&#xff0c;我们将构建一个自定义的文…

3D视觉引导机器人提升生产线的自动化水平和智能化程度

随着智能化技术的不断发展&#xff0c;汽车制造企业正积极寻求提升智能化水平的途径。富唯智能的3D视觉引导机器人抓取技术为汽车制造企业提供了一种高效、智能的自动化解决方案。 项目目标 某汽车制造企业希望通过引入智能化技术提升生产线的自动化水平和智能化程度。他们希望…

哎呦我, HashMap KeySet有序? 好像是哈

背景&#xff1a;有8个格子&#xff0c;上架物品时需要从第一个格子开始上架&#xff0c;不能跳格子&#xff0c;也就是说 如果格子1空着&#xff0c;就不能把物品放到格子2。有这么个顺序的情况 前人模块功能实现&#xff1a; 用HashMap 初始化格子信息&#xff0c;然后用 Ke…

2024年【T电梯修理】免费试题及T电梯修理考试总结

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 T电梯修理免费试题参考答案及T电梯修理考试试题解析是安全生产模拟考试一点通题库老师及T电梯修理操作证已考过的学员汇总&#xff0c;相对有效帮助T电梯修理考试总结学员顺利通过考试。 1、【多选题】TSGT7005-2012《…

基于vue3 + ant-design 使用阿里图标库iconfont.cn

对于使用 iconfont.cn 的用户&#xff0c;通过设置 createFromIconfontCN 方法参数对象中的 scriptUrl 字段&#xff0c; 即可轻松地使用已有项目中的图标。 组件封装 IconFont <template><IconFont :type"iconType" /> </template><script se…