【C++】C++11中的包装器和绑定器

news2024/9/23 13:29:38

目录

一、function包装器

1.1 可调用对象

1.2 概念

1.3 应用场景

二、bind绑定器


一、function包装器

1.1 可调用对象

我们平时使用的普通函数、函数指针、仿函数和Lambda表达式都是可调用对象,它们不仅可以作为其他函数的参数传入,还可以作为其他函数的返回值

虽然这些可调用对象的使用方法基本都差不多,但其定义方式大有不同,例如:

void swap(int& a, int& b) //普通函数
{
	int tmp = a;
	a = b;
	b = tmp;
}

void(*swap_p)(int&, int&) = swap; //函数指针

class Swap
{
public:
	void operator()(int& a, int& b) //仿函数
	{
		int tmp = a;
		a = b;
		b = tmp;
	}
};

auto lam_swap = [](int& a, int& b)-> void { //Lambda表达式
	int tmp = a;
	a = b;
	b = tmp;
};

这就导致我们在对这些不同的函数包装器进行保存的时候无法以统一的方式保存

例如:

假设一个函数模板的参数需要我们传入一个可调用对象,当我们传入不同的可调用对象时,这个函数也会被实例化多次,例如:

template<class F>
int tem_func(F f)
{
	static int count = 0;
	cout << "count=" << ++count << endl; //打印count的值
	cout << "&count=" << &count << endl; //打印count的地址
	return f();
}

int func()
{
	return 1;
}

class Func
{
public:
	int operator()()
	{
		return 1;
	}
};

auto lam_func = []()->int {return 1; };

可以看到,count是一个静态变量,但却有三个不同的地址,说明函数模板被实例化了三次

于是C++11引入了一种适配器,即function包装器,能够以统一的方式来保存这些可调用对象 

1.2 概念

std::function包装器是C++11引入的一个针对可调用对象的适配器,其本质是一个类模板

我们可以把包装器看作一个壳子,内部存放不同类型的可调用对象,这样对外看来它们都是一样的

要使用function包装器,首先得引入头文件<functional>

具体使用方法也并没有那么复杂,例如:

void swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	function<void(int&, int&)> f1 = swap;
	return 0;
}

可以看到,我们只需要以这种方式传入可调用对象的返回值类型和参数类型,即可将一个可调用对象用包装器包装起来

不止是普通函数,其他可调用对象也是一样

void swap(int& a, int& b) //普通函数
{
	int tmp = a;
	a = b;
	b = tmp;
}

void(*swap_p)(int&, int&) = swap; //函数指针

class Swap
{
public:
	void operator()(int& a, int& b) //仿函数
	{
		int tmp = a;
		a = b;
		b = tmp;
	}
};

auto lam_swap = [](int& a, int& b)-> void { //Lambda表达式
	int tmp = a;
	a = b;
	b = tmp;
};

int main()
{
	function<void(int&, int&)> f1 = swap;
	function<void(int&, int&)> f2 = swap_p;
	function<void(int&, int&)> f3 = Swap();
	function<void(int&, int&)> f4 = lam_swap;
	return 0;
}

通过使用function包装器对可调用对象进行统一方式的包装,就可以避免前面的问题了:

1.3 应用场景

OJ链接:LCR 036. 逆波兰表达式求值 - 力扣(LeetCode)

在这道题的核心思想是使用栈,遍历tokens,遇到数字则入栈,遇到运算符则取出栈顶的两个数字进行运算,并将结果重新入栈

以前,我们需要通过switch语句判断符号的类型并运算,代码重复度高,不够优雅

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        for(auto &s : tokens)
        {
            if(s == "+" || s == "-" || s == "*" || s == "/")
            {
                int r = st.top();
                st.pop();
                int l = st.top();
                st.pop();
                cout << l << " " << r << endl;
                switch(s[0])
                {
                case '+':
                    st.push(l + r);
                    break;
                case '-':
                    st.push(l - r);
                    break;
                case '*':
                    st.push(l * r);
                    break;
                case '/':
                    st.push(l / r);
                    break;
                }
            }
            else
                st.push(stoi(s));
        }
        return st.top();
    }
};

现在我们就可以结合map、lambda表达式和function包装器重新做一下这道题了

代码如下:

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        map<string, function<int(int, int)>> cmd = {
            {"+", [](int a, int b)->int { return a + b; }},
            {"-", [](int a, int b)->int { return a - b; }},
            {"*", [](int a, int b)->int { return a * b; }},
            {"/", [](int a, int b)->int { return a / b; }}
        };
        stack<int> st;
        for(auto &s : tokens)
        {
            if(cmd.count(s))
            {
                int r = st.top();
                st.pop();
                int l = st.top();
                st.pop();
                st.push(cmd[s](l, r));
            }
            else
                st.push(stoi(s));
        }
        return st.top();
    }
};

需要注意的是,当我们使用包装器包装类成员函数的时候,需要加上类域并且取地址

对于非静态的类成员函数,除了其显式定义的参数外,还需要在参数中加上this指针,例如:


二、bind绑定器

std::bind是一个函数模板,可以接受一个可调用对象,并生成一个新的可调用对象

有人说了,这不是多此一举吗?实际上我们可以通过这种方式来完成修改原来的可调用对象参数的效果

例如我们想修改某个函数的顺序:

其中placeholders::_2代表原函数的第二个参数,placeholders::_1代表原函数的第一个参数

_n作为占位符代表了原来的可调用对象在bind之后生成的可调用对象的参数顺序

又例如我们想让某个参数是固定的值,就可以直接在bind中设置该参数的值

这种方式就能解决上面提到的function包装器包装非静态类成员函数时每次都要添加this指针的问题

例如:

完.

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

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

相关文章

逻辑回归模型构建+PDP(部分依赖图)解析——Python代码及运行结果分析

一、逻辑回归模型简介 逻辑回归是一种广泛用于二分类问题的统计模型。它通过使用逻辑函数将预测结果映射到0到1之间&#xff0c;从而可以用于概率预测。模型的训练过程通常包括以下几个步骤&#xff1a; 数据预处理&#xff1a;处理缺失值、编码分类变量、标准化数值变量。特…

Python代码之特征工程基础

1. 什么是特征工程 特征工程是指从原始数据中提取、转换和创建适合于模型训练的数据特征的过程。它是机器学习和深度学习中非常重要的一步&#xff0c;因为好的特征工程可以显著提高模型的性能。特征工程涉及从数据中提取有意义的信息&#xff0c;并将其转换为模型可以理解和使…

[CP_AUTOSAR]_通信服务_DCM模块(二)_通用设计元素

目录 1、通用设计元素1.1、子模块1.2、NRC&#xff08;Negative Response Code&#xff09;1.3、Non-volatile 信息1.4、Types1.4.1、Atomic types overview1.4.2、Data array types overview1.4.3、Nested Data types overview1.4.4、Data types constraints1.4.5、Dcm_OpStat…

第一周、、

7-1 入度与出度 分数 10 全屏浏览 切换布局 作者 黄龙军 单位 绍兴文理学院 求有向图G中各顶点的入度与出度。建议分别采用邻接矩阵和邻接表这两种不同的存储结构完成。 输入格式: 首先输入一个正整数T&#xff0c;表示测试数据的组数&#xff0c;然后是T组测试数据。每组…

2024年,这4款思维导图在线工具帮你高效作图

思维导图是一种强大的思维工具&#xff0c;它能够帮助我们更好地处理信息、解决问题。很多人都不知道要怎么制作&#xff0c;我整理的这4款工具是目前很受欢迎的思维导图工具&#xff0c;使用起来也很简单。 1、福昕导图软件 传送门&#xff1a;pdf365.cn/naotu 这是一款制作…

kubernetes 集群组件介绍

kubernetes 集群组件介绍 Kubernetes 架构 在Kubernetes&#xff08;k8s&#xff09;集群中&#xff0c;主节点&#xff08;Master Node&#xff09;和工作节点&#xff08;Worker Node&#xff09;都运行特定的软件组件&#xff0c;它们共同管理和运行容器化的应用程序。以下…

SD卡参数错误:深度解析与数之寻软件恢复实战

一、SD卡参数错误&#xff1a;数据与设备的隐形杀手 在数字化时代&#xff0c;SD卡作为便携存储设备&#xff0c;广泛应用于相机、手机、无人机及各类电子设备中&#xff0c;承载着人们珍贵的照片、视频、文档等重要数据。然而&#xff0c;SD卡在使用过程中&#xff0c;有时会…

天津有哪些SOLIDWORKS代理商?

随着数字化转型的步伐加快&#xff0c;越来越多的企业开始寻求高效的三维设计解决方案来提高生产力和创新力。在天津&#xff0c;亿达四方作为SOLIDWORKS官方授权的代理商&#xff0c;正成为众多企业的首选合作伙伴。本文将详细介绍亿达四方提供的服务和优势&#xff0c;帮助您…

CTFHUB-web-RCE-过滤运算符

开启题目 查看源码发现管道符被过滤&#xff0c;使用分号&#xff1b;拼接注入&#xff0c;发现了 flag 的可疑文件 127.0.0.1;ls 使用 cat 查看 flag 文件&#xff0c;右键查看页面源代码发现了 flag 127.0.0.1;cat flag_229701159030749.php

IP地址申请SSL证书,实现https访问

一般情况下&#xff0c;SSL证书都是通过域名来申请的&#xff0c;但是很多单位没有域名或者不方便提供域名&#xff0c;只能提供IP地址&#xff0c;那么如果想实现IP地址的https访问&#xff0c;就得申请IP地址专用SSL证书&#xff0c;市面上专供IP地址使用的SSL证书服务商比较…

数字孪生平台:构建智慧未来,重塑空间智能生态的钥匙

数字孪生平台这一创新概念的勃然兴起&#xff0c;不仅是技术的飞跃&#xff0c;更是对空间信息处理与决策智慧化、生态构建的一次世代跃迁跃进。本文旨在深度剖析数字孪生平台的内核、运作机制、应用前景及其对智慧生态的深远影响。 数字孪生平台&#xff1a;定义与构想 数字…

【Python基础】Python六种标准数据类型中哪些是可变数据,哪些是不可变数据

文章目录 1.基本介绍可变数据类型不可变数据类型2.可变和不可变到底指的是什么?可变(Mutable)不可变(Immutable)总结1.基本介绍 Python 中的六种标准数据类型分为可变数据类型和不可变数据类型。以下是这些数据类型的分类: 可变数据类型 列表(List) 列表是一种有序集…

分类预测 | Matlab实现PSO-XGBoost粒子群算法优化XGBoost的多特征分类预测

分类预测 | Matlab实现PSO-XGBoost粒子群算法优化XGBoost的多特征分类预测 目录 分类预测 | Matlab实现PSO-XGBoost粒子群算法优化XGBoost的多特征分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现PSO-XGBoost粒子群算法优化XGBoost的多特征分类预测&a…

【Python】一文向您详细介绍 *(星号)和 **(双星号)

&#x1f680;【Python】一文向您详细介绍 *&#xff08;星号&#xff09;和 **&#xff08;双星号&#xff09; 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387;…

什么是网格策略?高抛低吸神奇—网格交易

网格交易主要是根据行情的波动&#xff0c;自动进行短线的高抛低吸操作&#xff0c;从而达到止损 止盈或者降低持仓成本的目的。 网格策略&#xff0c;是一种利用‘交易档位’模式对标的进行机械式买入卖出操作的量化 策略&#xff0c;是一个适用于震荡行情的经典策略。 适合做…

防火墙标签解决轮询错误

接上文&#xff08;LVS实验——部署DR模式集群&#xff09;&#xff0c;以http和https为例&#xff0c;当在RS中同时开放80和443端口&#xff0c;那么默认控制是分开轮询的&#xff0c;这样就出现了一个轮询错乱的问题 当第一次访问80被轮询到RS2后下次访问443仍然可能会被轮询…

一键翻译 | 分享一个更高级、更AI的翻译插件

最近AutoGPT不是更新了嘛 我也打算搭建一个来玩玩。 不过呢&#xff0c;官方文档都是英文&#xff0c;阅读起来还是比较费劲的 之前用的翻译插件实在难用&#xff0c;即卡而且翻译不准 在网上找了一个新的AI翻译插件&#xff0c;发现贼好用&#xff0c;速度上很快&#xff…

鹏鼎控股:最新面试求职SHL逻辑测评笔试题库讲解及真题分享

鹏鼎控股&#xff08;深圳&#xff09;股份有限公司&#xff0c;成立于1999年4月29日&#xff0c;是一家专业从事印制电路板&#xff08;PCB&#xff09;设计、研发、制造与销售的企业。公司产品广泛应用于通讯、消费电子、汽车、服务器等多个领域&#xff0c;服务全球市场。鹏…

猫咪除浮毛攻略,口碑好的宠物空气净化器推荐

养猫咪的幸福与苦恼是同时存在的&#xff0c;一边喜爱它柔软的毛发&#xff0c;一边担心它乱飞的浮毛。无论是客厅、卧室还是书房&#xff0c;只要有猫咪活动的地方&#xff0c;都可能有毛发、皮屑等痕迹。铲屎官平日的清理主要是对家具、地板表面&#xff0c;去除可见的宠物毛…

Prometheus安装,带资源

文章目录 1.简述2.安装node_expoter3.安装redis_expoter&#xff08;非必需&#xff09;4.SpringBoot&#xff08;非必需&#xff09;5.安装prometheus6.查看prometheus的Targets7.安装grafana 资源下载-资源下载-资源下载 提取码: i97g 1.简述 样例会安装以下组件&#xff0…