C++ function<>和bind()

news2025/1/12 18:03:55

一、可调用对象

介绍两个概念:调用运算符和可调用对象

  • 调用运算符

       调用运算符,即:() 。跟随在函数名之后的一对括号 “()”,起到调用函数的效果,传递给函数的参数放置在括号内。

  • 可调用对象

        对于一个对象或者一个表达式,如果可以对其使用调用运算符,则这个对象或者表达式可被称为可调用对象。所以可调用对象看起来就是可以像函数一样调用的对象。

         分类

     可调用对象可以分为如下几类:

  • 普通函数和函数指针
  • 仿函数(functor(仿函数), 或者称之为function object(函数对象))

          仿函数(functor)是一个结构体或者类,只不过它重载了'()'操作符,使得这个结构体或者类可以像函数一样被调用。仿函数也叫函数对象。

  • lambda函数
  • 类成员函数指针或者类成员指针

二、std::function

      可调用对象的定义方式比较多,但是函数的调用方式都是类似的。比如:经常遇到将可调用类型作为参数传递的情况,如果针对不同的可调用类型进行单独声明,则函数参数只能接收某种具体类型,这非常的不灵活,所以需要使用一种统一的方式保存可调用对象或者传递可调用对象,于是就有了std::function。

     std::function是一个可调用对象包装器,它是一个类模板,它通过指定模版参数,用统一的方式来处理各种可调用对象。

std::function<return_type(parameter_types)> var_name;

    其模板参数是函数签名:比如,void()表示一个函数,不接收参数,也不接收返回值;int(int,int)代表某函数,它接收两个参数并返回int值。假设我们要构建function<>实例,那么,由于模板参数先行指定了函数签名,因此指向的函数必须与之相符。即它应该接收指定类型的参数,返回值也必须可以转换为指定类型的可调用对象。

     std::function是定义在 <functional>这个头文件中的,所以如果要想使用std::function则需要先包含这个头文件:#include <functional>

1.包装普通函数或者函数指针

#include <iostream>
#include <functional>

using namespace std;

int add(int a, int b) {
	return a + b;
}

int main() {
	//普通函数
	function<int(int, int)> f = add;
	cout << "sum is " << f(5, 8) << endl;

	//定义一个函数指针,并将其指向一个函数
	int (*fptr)(int, int);
	fptr = add;
	function<int(int, int)> ftr = fptr;
	cout << "(fptr)sum is " << f(5, 79) << endl;

	return 0;
}

2. 包装仿函数

下面给出了一个实例代码, 定义了一个class,名叫AddTwoNum,因为其重载了‘()’操作符,所以这个类定义的实例对象是一个仿函数 (或叫函数对象),在本例中,这个仿函数返回值是其两个int类型成员的数值之和。

#include <iostream>
#include <functional>

using namespace std;

class AddTwoNum {
public:
	int	operator()() { return a + b; }
	AddTwoNum(int x, int y) :a(x), b(y) {}
	AddTwoNum() { a = 0, b = 0; }
private:
	int a;
	int b;
};

int main() {
	AddTwoNum add(100,200);
	
	function<int(void)> f = add;
	cout << f() << endl;

	return 0;
}

3. 包装lamda函数

#include <iostream>
#include <functional>

using namespace std;

int main() {

	function<int(int, int)> f1 ;
	f1 = [](int a, int b) {
		return a + b; };

	cout << f1(10, 50) << endl;

	return 0;
}

4. 包装类的静态成员函数

        因为class的静态成员函数不需要类对象实例或者类对象指针也可以调用,所以function也可以包装类的静态成员函数。

#include <iostream>
#include <functional>

using namespace std;

class AddTwoNum {
public:
	static int	Sum(int a, int b) { return a + b; }
};

int main() {

	function<int(int, int)> f = &AddTwoNum::Sum;
	cout << f(50, 80) << endl;

	return 0;
}

5. 包装类的普通成员函数

       function是无法直接包装类的普通成员函数,因为类普通成员函数指针是需要类对象参与才能完成的但是结合后面要介绍的bind()一起是可以实现包装类的普通成员函数的,这个在后面bind()那一节会看到。

std::function对象实例可被拷贝和移动,并且可以使用指定的调用特征来直接调用目标元素。当std::function对象实例未包含任何实际可调用实体时,调用该std::function对象实例将抛出std::bad_function_call异常。  

三、std::bind()

       std::bind()是一个通用的函数适配器,它也是包含在<functional>这个头文件里的。std::bind()的使用方式如下面所示,std::bind将可调用对象与其参数一起进行绑定,生成一个新的可调用对象来适应原对象的参数列表,绑定后的结果可以使用前面介绍的std::function保存。这时我们调用 newCallable,newCallable 就会调用 callable, 并用 arg_list 传递参数。

    auto newCallable = bind(callable,  arg_list);

         另外,如果函数有多个参数,bind()可以绑定部分参数,其他的参数可以等到调用时指定。bind()在绑定参数时需要通过占位符std::placeholder::_x来决定bind()参数列表里所在的位置的参数在调用发生时将会属于第几个参数:_x表示外部传参时,调用者所传的实参列表里的第x个参数需要作为形参所在的这个位置的参数,下面举个例子: 

          下面这个例子是一个将参数列表里的参数打印出来的一个函数printF(),我们通过bind()绑定其部分参数,和参数顺序来生成几个不同的新的可调用对象。

  • f1其实和原先的printF没有差别,参数个数和参数顺序都是一样的;
  • f2有先绑定第三个参数,即第三个参数已经指定为一个固定的值--->3,所以新生成的可调用对象在调用时,只需要传入前两个参数的值即可;
  • f3虽然也要像原先的printF()那样传入三个参数,可是由于placeholders有改变参数的顺序,已传入的第一个参数为例,因为f3的第1个参数有标明placeholders::_3,这就表示,调用f3()时传入的第3个参数会被作为原先可调用对象printF()的第一个参数。
  • f4也是通过palceholders改变了原先的参数顺序,而且还先绑定了第2个参数的值为3
#include <iostream>
#include <functional>

using namespace std;

void printF(int x, int y, int z) {
	cout << "x="<<x << ", y=" << y << ", z=" << z<<endl;
}

int main() {
	
	auto f1 = bind(printF,placeholders::_1, placeholders::_2, placeholders::_3);
	cout << "f1(1,2,3)-----> "; //x=1 ,y=2, z=3
	f1(1, 2, 3);

	auto f2 = bind(printF, placeholders::_1, placeholders::_2, 3);
	cout << "f2(1,2)  -----> "; //x=1 ,y=2, z is always 3
	f2(1, 2);

	auto f3 = bind(printF, placeholders::_3, placeholders::_1, placeholders::_2);
	cout << "f3(1,2,3)-----> "; //x=3 ,y=1, z=2
	f3(1, 2, 3);

	//
	auto f4 = bind(printF, placeholders::_2, 3, placeholders::_1);
	cout << "f4(1,2)  -----> "; //x=2 ,y is always 3, z=1
	f4(1, 2);

	return 0;
}

bind()绑定类的普通成员函数

bind()如果绑定的类的普通成员函数的话,需要传入该类的类对象或者类对象指针 

#include <iostream>
#include <functional>

using namespace std;

class AddTwoNum {
public:
	int	Sum() { return a + b; }
	AddTwoNum(int x, int y) :a(x), b(y) {}
	AddTwoNum() { a = 0, b = 0; }
private:
	int a;
	int b;
};

int main() {
	AddTwoNum add(100, 200);
	//对象形式调用成员函数
	auto f1 = bind(&AddTwoNum::Sum,add);
	cout << f1() << endl;

	//指针形式调用成员函数
	auto f2 = bind(&AddTwoNum::Sum, &add);
	cout << f2() << endl;

	return 0;
}

bind()绑定类的静态成员函数

      因为即使是在没有实例化的类对象的条件下,类的static成员函数也是可以被调用的,所以bind()绑定的是类的静态成员函数时,不用指定类对象或类对象指针。

#include <iostream>
#include <functional>

using namespace std;

class AddTwoNum {
public:
	static int	Sum(int a,int b) { return a + b; }
};

int main() {

	auto f1 = bind(&AddTwoNum::Sum, placeholders::_1, placeholders::_2);
	cout << f1(20,30) << endl;

	return 0;
}

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

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

相关文章

位图+布隆过滤器+海量数据问题(它们都是哈希的应用)

一)位图: 首先计算一下存储一下10亿个整形数据&#xff0c;需要多大内存呢&#xff0c;多少个G呢&#xff1f; 2^3010亿&#xff0c;10亿个字节 byte kb mb gb 100000000个字节/1024/1024/10241G 所以10亿个字节就是1G&#xff0c;所以40亿个字节就是4G&#xff0c;也就是10个整…

Swing基本组件的用法(一)

语雀笔记&#xff1a;https://www.yuque.com/huangzhanqi/rhwoir/paaoghdyv0tgksk1https://www.yuque.com/huangzhanqi/rhwoir/paaoghdyv0tgksk1Java图形化界面: Java图形化界面学习demo与资料 (gitee.com)https://gitee.com/zhanqi214/java-graphical-interface Swing组件层次…

机器学习笔记 - 视频分析和人类活动识别技术路线简述

一、理解人类活动识别 首先了解什么是人类活动识别,简而言之,是对某人正在执行的活动/动作进行分类或预测的任务称为活动识别。 我们可能会有一个问题:这与普通的分类任务有什么不同?这里的问题是,在人类活动识别中,您实际上需要一系列数据点来预测正确执行的动作。 看看…

Python 多进程异常

这里写目录标题 1、捕获异常2、退出程序3、进程共享变量4、multiprocessing的Pool所起的进程中再起进程 1、捕获异常 https://zhuanlan.zhihu.com/p/321408784 try:<语句> except Exception as e:print(异常说明,e)1 捕获所有异常 包括键盘中断和程序退出请求&#xff0…

一个Binder的前生今世 (一):Service的创建

一个Binder的前生今世 (一):Service的创建 一个Binder的前生今世Binder的历史 (字面意义的前生今世)Binder的生命周期(抽象意义的前生今世)Binder 应用及系统层关系图Binder应用层的架构设计Binder应用层实现Binder的创建服务端Binder的创建服务端Binder的传递Binder在客…

Trino HTTPS 与密码认证介绍与实战操作

文章目录 一、概述二、安装 Trino三、配置 HTTPS1&#xff09;生成证书2&#xff09;配置 Trino3&#xff09;修改 Trino docker-compose yaml 文件4&#xff09;开始部署 Trino5&#xff09;测试验证 四、密码认证1&#xff09;开启密码认证2&#xff09;创建密码认证配置文件…

AndroidStudio 安装与配置【安装教程】

1.下载软件 进入官网https://developer.android.google.cn/studio&#xff0c;直接点击下载 2.阅读并同意协议书 直接下滑至最底部 如果这里出现了无法访问 官方地址&#xff1a;https://redirector.gvt1.com/edgedl/android/studio/install/2022.3.1.19/android-studio-2022.…

java:杨辉三角形

public class YangHui {public static void main(String[] args){int yangHui[][] new int[10][];for (int i 0; i < yangHui.length;i){yangHui[i] new int[i 1];for (int j 0; j < yangHui[i].length; j){ // 最初和最后的数值都是1if (j 0 || j …

LeetCode 847. Shortest Path Visiting All Nodes【状态压缩,BFS;动态规划,最短路】2200

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

深度解析shell脚本的命令的原理之pwd

pwd是Print Working Directory的缩写&#xff0c;是一个Unix和Linux shell命令&#xff0c;用于打印当前工作目录的绝对路径。以下是对这个命令的深度解析&#xff1a; 获取当前工作目录&#xff1a;pwd命令通过调用操作系统提供的getcwd&#xff08;或相应的&#xff09;系统调…

带自动采集小说网站源码 小说听书网站源码 小说网站源码 带教程

PTCMS可听书可下载的小说站源码 带自动采集和搭建视频教程 必装环境&#xff1a;Nginx(apache.iis也可)&#xff0c;mysql,php5.6,memcached php5.6安装扩展memcache新建站点&#xff0c;注意新建时&#xff0c;PHP版本必须选择PHP5.6 安装教程 1.上传网站文件到网站目录&…

看完这篇 教你玩转渗透测试靶机Vulnhub——Grotesque:1.0.1

Vulnhub靶机Grotesque&#xff1a;1.0.1渗透测试详解 Vulnhub靶机介绍&#xff1a;Vulnhub靶机下载&#xff1a;Vulnhub靶机安装&#xff1a;①&#xff1a;信息收集&#xff1a;②&#xff1a;漏洞利用GetShell&#xff1a;③&#xff1a;Keepass文件的解密&#xff1a;④&…

科技抗老新突破,香港美容仪品牌内地重磅上市

近年来&#xff0c;新消费时代“颜值经济”的火热促使美容行业市场规模增长迅速&#xff0c;越来越多的人愿意为“美”买单&#xff0c;对美的需求也随之增长&#xff0c;美容行业已经成为成长最快的新锐产业。随着经济和科技的发展&#xff0c;“快捷”也成为了当今社会的时代…

【网络】计算机网络基础

Linux网络 对网络的理解 在网络传输中存在的问题&#xff1a; 找到我们所需要传输的主机解决远距离数据传输丢失的问题怎么进行数据转发&#xff0c;路径选择的问题 有问题&#xff0c;就有解决方案&#xff1b; 我们把相同性质的问题放在一起&#xff0c;做出解决方案 解…

【C++】深拷贝和浅拷贝 ③ ( 浅拷贝内存分析 )

文章目录 一、浅拷贝内存分析1、要分析的代码2、调用有参构造函数创建 Student 实例对象3、调用默认拷贝构造函数为新对象赋值4、修改拷贝对象成员变量指针指向的数据5、析构报错 一、浅拷贝内存分析 1、要分析的代码 下面的代码中 , 没有定义拷贝构造函数 , 因此 C 编译器会自…

无涯教程-JavaScript - CSC函数

描述 CSC函数返回以弧度指定的Angular的余割值。 语法 CSC (number)争论 Argument描述Required/OptionalNumberThe angle (in radians) that you want to calculate the cosecant of.Required Notes CSC(n)等于1/SIN(n) 如果Angular为度,则将其乘以PI()/180或使用RADIANS…

每日一题 198. 打家劫舍

难度&#xff1a;中等 这是昨天的每日一题忘记做了&#xff0c;没想到正好是今天的题目的简化版&#xff0c;详细思路可以看http://t.csdn.cn/Smr0t 代码&#xff1a; class Solution:def rob(self, nums: List[int]) -> int:if len(nums) 1:return nums[0]a, b nums[0…

怒刷LeetCode的第3天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;动态规划 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;模拟 方法二&#xff1a;数学规律 方法三&#xff1a;分组 第三题 题目来源 题目内容 解决方法 方法一&#xff1a;数学方法 方法…

001-项目介绍

项目介绍 文章目录 项目介绍编写目的小小说明 项目介绍目前状态目录项目介绍第一代第二代第三代软件部署硬件篇 总结 关键字&#xff1a; Qt、 Qml、 分享、 记录、 目录 编写目的 这是我目前参与的一个真实项目&#xff0c;而且我非常幸运地能够从头到尾地参与其中。在面…

微信小程序|自定义弹窗组件

目录 引言小程序的流行和重要性自定义弹出组件作为提升用户体验和界面交互的有效方式什么是自定义弹出组件自定义弹出组件的概念弹出层组件在小程序中的作用和优势为什么需要自定义弹出组件现有的标准弹窗组件的局限性自定义弹出组件在解决这些问题上的优势最佳实践和注意事项避…