std::bind的讲解

news2024/12/28 2:57:29

一、在讲解std::bind之前,我们先来复习下std::function。

std::function 是一个“可调用对象”包装器,是一个类模板,可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。

什么是可调用对象?

(1)函数指针
(2)具有operator()成员函数的类对象(传说中的仿函数),lambda表达式
(3)可被转换为函数指针的类对象
(4)类成员(函数)指针
(5)bind表达式或其它函数对象

比如:

// 普通函数
int add(int a, int b){return a+b;} 

// lambda表达式
auto mod = [](int a, int b){ return a % b;}

// 函数对象类
struct divide{
    int operator()(int denominator, int divisor){
        return denominator/divisor;
    }
};

用std::function将上述类型保存起来:

std::function<int(int ,int)>  a = add; 
std::function<int(int ,int)>  b = mod ; 
std::function<int(int ,int)>  c = divide(); 

二、std::bind

可将std::bind函数看作一个通用的函数适配器,它接受一个“可调用对象”,生成一个新的可调用对象来“适应”原对象的参数列表。

std::bind主要有以下两个作用:
(1)将可调用对象和其参数绑定成一个防函数;
(2)只绑定部分参数,减少可调用对象传入的参数。

1、绑定普通函数

double divide (double x, double y) {
    return x/y;
}

auto divide_half = std::bind (my_divide,_1,2);  
std::cout << fn_half(10) << std::endl; // 输出5

(1)bind的第一个参数是函数名,普通函数做实参时,会隐式转换成函数指针。因此std::bind (divide,_1,2)等价于std::bind (&divide,_1,2);
(2)_1表示占位符,位于<functional>中,std::placeholders::_1;

2、绑定成员函数

#include <functional>
#include <iostream>

using namespace std;

struct Foo {
    void sum(int n1, int n2)
    {
        std::cout << n1+n2 << std::endl;
    }

	static int StaticMember(int a) { 
        return 66 + a; 
    }

    int data = 10;
};

int main() {
    Foo foo;
    auto f = std::bind(&Foo::sum, &foo, 95, std::placeholders::_1);
    f(5); // 输出100
}

(1)bind绑定类成员函数时,第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址。
(2)必须显示的指定&Foo::sum,因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在Foo::sum前添加&;
(3)使用对象成员函数的指针时,必须要知道该指针属于哪个对象,因此第二个参数为对象的地址 &foo;

3、绑定静态成员函数

	auto fun2 = &Foo::StaticMember;
    cout<<fun2(3)<<endl;

4、绑定成员变量

#include <functional>
#include <iostream>

using namespace std;

class TestClass4 {
public:
    TestClass4() : m_a(100)
    {}
	
public:
    int m_a;
};

int main() {
    TestClass4 test4;
    auto fun4 = std::bind(&TestClass4::m_a, std::placeholders::_1);

	int var = fun4(test4);
    std::cout<<var<<std::endl;
}

5、绑定仿函数

#include <functional>
#include <iostream>

using namespace std;

class TestClass5 {
public:
    TestClass5() = default;
	
    TestClass5(const TestClass5& obj) {
        std::cout<<"TestClass5 copy construct."<<std::endl;
    }
	
    void operator()(int a) {
        std::cout<<a<<std::endl;
    }
};

int main(){
    TestClass5 test5;
    auto fun5 = test5;
    fun5(2018);
}

6、绑定成员函数时,哪些做法会先调用“拷贝构造函数” ?

#include <functional>
#include <iostream>

using namespace std;

class TestClass {
public:
    TestClass(int a):m_a(a){}
    TestClass(const TestClass& obj) {
        m_a = obj.m_a + 100;
        std::cout<<"copy construct."<<std::endl;
    }
	
    int ClassMember(int a) {
	    std::cout<<" this:"<<this<<" :"<<&m_a<<" "<<m_a<<std::endl;
		return 55 + a; 
	}
	
    int ClassMember2(int a,char ch,float f) {
        std::cout <<ch <<" "<< f << " "<<a<<std::endl;
        return 55 + a;
    }
	
    static int StaticMember(int a) {
	    return 66 + a; 
	}

public:
    int m_a;
};;

int main(){
    TestClass test(67);
    std::cout<<"&test "<<&test<<" "<<test.m_a<<" &test.m_a "<<&test.m_a<<std::endl;
    std::cout<<"---------------------------------------------------------------------"<<std::endl;
	auto fun4 = std::bind(&TestClass::ClassMember, test, std::placeholders::_1); // 调用拷贝构造函数
	fun4(4);
	
	std::cout<<"---------------------------------------------------------------------"<<std::endl;
	auto fun5 = std::bind(&TestClass::ClassMember, &test, std::placeholders::_1); // 不调用拷贝构造函数
    fun5(5);
	
	std::cout<<"---------------------------------------------------------------------"<<std::endl;
	auto fun6 = &TestClass::StaticMember; // 不调用拷贝构造函数
    std::cout<<fun6(6)<<std::endl;
	
	std::cout<<"---------------------------------------------------------------------"<<std::endl;
    auto fun7 = std::bind(&TestClass::m_a, std::placeholders::_1); // 不调用拷贝构造函数
    std::cout<<fun7(test)<<" "<<test.m_a<<std::endl;
	
}

执行结果:

7、传引用参数的问题

当需要把对象传到bind中的参数中时,需要使用ref或者cref。

#include<iostream>
#include<functional>
 
using namespace std;
using namespace placeholders;
 
void alter(int &a, int b) {
    a = b;
}

int main() {
    int a = 5;
    auto f1 = bind(alter, a, _1);      //此处bind中的参数a只是a的一个拷贝,而不是a的引用
    f1(9);                           
    cout << a << endl;                //所以此处a仍为5
 
    auto f2 = bind(alter, ref(a), _1);  //使用ref可以将a的引用传递给bind
    f2(9);
    cout << a << endl;                //所以此处a变成了9
 
    return 0;
}
 
 

8、指向成员函数的指针

#include <iostream>
struct Foo {
    int value;
    void f1() { std::cout << "f1(" << this->value << ")\n"; }
    void f2() { std::cout << "f2(" << this->value << ")\n"; }
};

void apply(Foo* foo1, Foo* foo2, void (Foo::*fun)()) {
    (foo1->*fun)();  // call fun on the object foo1
    (foo2->*fun)();  // call fun on the object foo2
}

int main() {
    Foo foo1{1};
    Foo foo2{2};

    apply(&foo1, &foo2, &Foo::f1);
    apply(&foo1, &foo2, &Foo::f2);
}

(1)成员函数指针的定义:void (Foo::*fun)(),调用时传递的实参: &Foo::f;

(2)fun为类成员函数指针,所以调用时要通过解引用的方式获取成员函数*fun,即(foo1->*fun)()。

参考:

(1)C++11 中的std::function和std::bind

(2)c++bind函数使用

(3)第六节 std::bind 绑定器

(4)c++11随记:std::bind及 std::placeholders

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

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

相关文章

async创建异步任务

想让线程之间可以有两个数据的交换。之前一直采用的是全局变量互斥锁的方法。到目前为止&#xff0c;线程运行完之后还无法提供一个返回值。 此时引入 future 和 async。 sync为同步的意思&#xff0c;async为异步任务。同步任务前文已经结束过&#xff1a;他指的是两个人协同…

SpringBoot 日志文件:日志的作用?为什么要写日志?

文章目录 &#x1f387;前言1.日志长什么样子&#xff1f;2.自定义打印日志2.1 在程序中得到日志对象2.2 使用日志对象打印日志 3.日志级别3.1 日志级别的分类与使用3.2 日志级别有什么用呢&#xff1f;3.3 日志级别的设置 4.日志持久化保存5.更方便的日志输出5.1 添加 lombok …

Celery分布式异步框架

Celery异步任务框架 """ 1&#xff09;可以不依赖任何服务器&#xff0c;通过自身命令&#xff0c;启动服务(内部支持socket) 2&#xff09;celery服务为为其他项目服务提供异步解决任务需求的 注&#xff1a;会有两个服务同时运行&#xff0c;一个是项目服务&a…

Android 自定义View和事件分派 图解

Android 自定义View和事件分派 图解_猎羽的博客-CSDN博客https://blog.csdn.net/feather_wch/article/details/131487012

并查集和LRUCache

目录 1. 并查集 1.1原理 1.2实现 1.3应用 1.3.1省份数量 1.3.2等式方程的可满足性 2.LRUCache 1.概念 2.实现 3.JDK中类似LRUCahe的数据结构LinkedHashMap 4.LRU Cache的OJ 1. 并查集 1.1原理 把不同的元素划分到不想交的集合.开始时,每个元素自成一个单元集合,然后…

OSGI-Bundle:概念和入门

OSGI(Open Service gateway initactive)是java动态化模块系统的一系列规范。即一个系统应用上可以有很多可插拔的小应用&#xff0c;整个应用能运行和协调&#xff0c;小应用之间也可以相互交互完成业务需求。 Bundle: bundle 是以 jar 包形式存在的一个模块化物理单元&#x…

Ceph:关于 Ceph 用户认证授权管理的一些笔记

写在前面 准备考试&#xff0c;整理 Ceph 相关笔记博文内容涉及, Ceph 用户管理&#xff0c;认证管理&#xff0c;权限管理 以及相关 Demo理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#xff0c;全心全意&…

antdesginVue a-date-picker(日期时间选择器)禁用当前时间之前的时间,包含时分秒

antdesginVue a-date-picker(日期时间选择器)禁用当前时间之前的时间&#xff0c;包含时分秒 话不多说直接上效果 <a-form-item label"发生时间" name"start_time"><a-date-pickerstyle"width: 100%"allowClearv-model:value"f…

C++模板进阶知识

文章目录 前言模板进阶1.非类型模板参数2.模板的特化2.1概念2.2函数模板特化2.3类模板特化2.3.1 全特化2.3.2 偏特化2.3.3 类模板特化应用示例 3.模板的分离编译3.1 什么是分离编译3.2 模板的分离编译3.3 解决方法 4 模板总结 后记 前言 之前我们讲过模板初阶的知识&#xff0…

Linux 解决root用户被限制连接服务器

Linux 解决root用户被限制连接服务器 1. 问题描述2. 解决问题2.1 方式一&#xff08;忘记root密码的情况&#xff09;2.2 方式二&#xff08;知道root密码的情况&#xff09; 3. 其他 1. 问题描述 使用 root 用户不能链接服务器&#xff0c;密码对&#xff0c;就是连接不上&am…

uniapp:分享一个自定义侧滑样例

首先看html,分为两部分&#xff0c;主体内容部分和功能部分&#xff0c;功能部分在css中定位到主体部分的右边 <view class"section" ref"box_center" touchstart"drawStart" touchmove"drawMove($event)"touchend"drawEnd($…

晨控智能UWB室内定位:工厂智能化的新引擎

晨控智能UWB室内定位&#xff1a;工厂智能化的新引擎 工厂是一个复杂而庞大的环境&#xff0c;通常包括多个车间、设备、人员以及大量的物料和产品。需要实时、准确的定位数据来支持各项运营活动。然而&#xff0c;传统的定位技术无法满足工厂内部的高精度定位需求。而UWB室内…

u-boot的烧写及使用,u-boot-2013.01的移植 6.30

1.将Linux的执行文件放到板子上运行 嵌入式系统 1.嵌入式系统 定制2.硬件&#xff1a;核心芯片底板软件&#xff1a;驱动应用 驱动系统应用&#xff08;并发&#xff0c;网络&#xff0c;文件。。。&#xff09;3.系统&#xff1a;linux 开源 模块化 支持芯片众多 功能…

针对字符串输入之间有空格的问题相关的问题

先说结论&#xff1a; bool flag true;while (cin >> s) {if (flag) {flag false;cout << s.size();} else {cout << , << s.size();}} 即用while&#xff08;cin>>s&#xff09;来输入&#xff0c;一段单词一段单词的来做&#xff08;遇到ci…

第十一章 原理篇:transformer模型入门

说在前面的话&#xff1a; 找工作面试不是特别顺利。进了目标公司的二面&#xff0c;但是一面面试官问的一些“新技术”问题答得不太好&#xff0c;尤其是transformer相关的。这一点确实是自己的问题&#xff0c;在工作后总是面向业务学习&#xff0c;对很多算法都是处于“听说…

AD从原理图到PCB超详细教程

AD超详细教程 前言一、建立一个工程模板二、原理图1.设计原理图。2.使用AD自带库和网上开源原理图库3.画原理图库4.编译原理图 三、PCB1.确定元器件尺寸大小2.绘制PCB Library①使用元器件向导绘制元件库②原理图与PCB的映射 3.绘制PCB①更新PCB②调整元件位置③布线④漏线检查…

库操作和表操作(数据库系列2)

目录 前言&#xff1a; 1.数据库的操作 1.1显示当前的数据库 1.2创建数据库 1.3使用数据库 1.4删除数据库 2.常用数据类型 2.1数值类型 2.2字符串类型 2.3日期类型 3.表的操作 3.1查看表结构 3.2创建表 3.3查看表 3.4删除表 结束语&#xff1a; 前言&#xff1…

【硬件自动化测试--测试软件的设计及实现】如何设计并实现!

今天来聊聊关于硬件方向的自动化软件设计及实现,后面我会用实例来让我们更加深入的了解硬件自动化,首先开发工具选择的是python语言,为啥选择python语言呢,因为他的语法比较简洁,外置库非常多,反正就是对于做自动化方面很实用就对了。 1.硬件自动化测试大致分为三个阶段实…

拓展:IDEA如何使用不同版本的JDK?(改了还报错很可能因为没改全,以mac为例)

以下面的案例为例 Enhanced ‘switch’ blocks are not supported at language level ‘8’ 后面知道是因为Spring的版本和JDK的版本不对应&#xff0c;结果网上找到的解决方案都很简单。下载了一个新版本的JDK&#xff0c;然后IDEA里面Project Structure的Project标签里把SDK给…

ubuntu的aarch64版本上安装anaconda

ubuntu的aarch64版本上安装anaconda 问题背景&#xff1a;今天在基于docker安装的ubuntu18-04的版本上想要安装anaconda&#xff0c;但是出现了问题&#xff0c;发现ubuntu的版本18-04对应的是aarch64&#xff0c;因此记录安装方法。 首先下载安装包没问题但是&#xff0c;在具…