C++——包装器std::function与绑定器std::bind

1、可调用对象的包装器

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

1.1基本用法

使用std::function必须包含一个头文件叫做functional,具体语法如下

#include <functional>
std::function<返回值类型(参数类型列表)> name = 可调用对象;

示例:

#include <iostream>
#include <functional>
using namespace std;int add(int a, int b)
{cout << a << " + " << b << " = " << a + b << endl;return a + b;
}class T1
{public:static int sub(int a, int b){cout << a << " - " << b << " = " << a - b << endl;return a - b;}int operator()(int a, int b){cout << a << " * " << b << " = " << a * b << endl;return a * b;}
};class T2
{public:int operator()(int a, int b){cout << a << " * " << b << " = " << a * b << endl;return a * b;}double operator()(int a, double b){cout << a << " + " << b << " = " << a + b << endl;return a + b;}
};int main(void)
{// 绑定一个普通函数function<int(int, int)> f1 = add;// 绑定以静态类成员函数T1 c;function<int(int, int)> f2 = T1::sub;function<int(int, int)> f4 = c;//可自主识别对象里的仿函数// 绑定一个仿函数T2 t;function<double(int, double)> f3 = t;//可自主识别对象里的仿函数// 函数调用f1(9, 3);f2(9, 3);f4(9, 3);f3(9, 3);return 0;
}

可自主识别对象里的仿函数,通过测试代码可以得到结论:std::function 可以将可调用对象进行包装,得到一个统一的格式,包装完成得到的对象相当于一个函数指针,和函数指针的使用方式相同,通过包装器对象就可以完成对包装的函数的调用了。

1.2作为回调函数使用

#include <iostream>
#include <functional>
using namespace std;class A
{public:// 构造函数参数是一个包装器对象A(const function<void()>& f) : callback(f){}void notify(){callback(); // 调用通过构造函数得到的函数指针}
private:function<void()> callback;
};class B
{public:void operator()(){cout << "sssssssssssssssssss!!!" << endl;}
};
int main(void)
{B b;A a(b); // 仿函数通过包装器对象进行包装a.notify();return 0;
}

通过上一个示例得到,A a(b);可自动识别出仿函数并包装作为函数参数,最终输出如下:

通过上面的例子可以看出,使用对象包装器 std::function 可以非常方便的将仿函数转换为一个函数指针,通过进行函数指针的传递,在其他函数的合适的位置就可以调用这个包装好的仿函数了。

另外,使用 std::function 作为函数的传入参数,可以将定义方式不相同的可调用对象进行统一的传递,这样大大增加了程序的灵活性。

2、 绑定器

std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。通俗来讲,它主要有两大作用:

将可调用对象与其参数一起绑定成一个仿函数。
将多元(参数个数为n,n>1)可调用对象转换为一元或者(n-1)元可调用对象,即只绑定部分参数。
绑定器函数使用语法格式如下:

// 绑定非类成员函数/变量
auto f = std::bind(可调用对象地址, 绑定的参数/占位符);
// 绑定类成员函/变量
auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);

示例:

在下面的程序中,使用了 std::bind 绑定器,在函数外部通过绑定不同的函数,控制了最后执行的结果。std::bind绑定器返回的是一个仿函数类型,得到的返回值可以直接赋值给一个std::function,在使用的时候我们并不需要关心绑定器的返回值类型,使用auto进行自动类型推导就可以了。

placeholders::\_1 是一个占位符,代表这个位置将在函数调用时被传入的第一个参数所替代。同样还有其他的占位符 placeholders::\_2、placeholders::\_3、placeholders::\_4、placeholders::\_5 等……

#include <iostream>
#include <functional>
using namespace std;void callFunc(int x, const function<void(int)>& f)
{if (x % 2 == 0){f(x);}
}void output(int x)
{cout << x << " ";
}void output_add(int x)
{cout << x + 10 << " ";
}int main(void)
{// 使用绑定器绑定可调用对象和参数auto f1 = bind(output, placeholders::_1);for (int i = 0; i < 10; ++i){callFunc(i, f1);}cout << endl;auto f2 = bind(output_add, placeholders::_1);for (int i = 0; i < 10; ++i){callFunc(i, f2);}cout << endl;return 0;
}

再看示例:

#include <iostream>
#include <functional>
using namespace std;void output(int x, int y)
{cout << x << " " << y << endl;
}int main(void)
{// 使用绑定器绑定可调用对象和参数, 并调用得到的仿函数bind(output, 1, 2)();bind(output, placeholders::_1, 2)(10);bind(output, 2, placeholders::_1)(10);// error, 调用时没有第二个参数// bind(output, 2, placeholders::_2)(10);// 调用时第一个参数10被吞掉了,没有被使用bind(output, 2, placeholders::_2)(10, 20);bind(output, placeholders::_1, placeholders::_2)(10, 20);bind(output, placeholders::_2, placeholders::_1)(10, 20);return 0;
}

解释:

1 2 bind(output, 1, 2)();
10 2 bind(output, placeholders::_1, 2)(10);
2 10 bind(output, 2, placeholders::_1)(10);
2 20 bind(output, 2, placeholders::_2)(10, 20);
10 20 bind(output, placeholders::_1, placeholders::_2)(10, 20);
20 10 bind(output, placeholders::_2, placeholders::_1)(10, 20);

通过测试可以看到,std::bind 可以直接绑定函数的所有参数,也可以仅绑定部分参数。在绑定部分参数的时候,通过使用 std::placeholders 来决定空位参数将会属于调用发生时的第几个参数。

可调用对象包装器 std::function 是不能实现对类成员函数指针或者类成员指针的包装的,但是通过绑定器 std::bind 的配合之后,就可以完美的解决这个问题了,再来看一个例子,然后再解释里边的细节:

#include <iostream>
#include <functional>
using namespace std;class Test
{public:void output(int x, int y){cout << "x: " << x << ", y: " << y << endl;}int m_number = 100;
};int main(void)
{Test t;// 绑定类成员函数function<void(int, int)> f1 = bind(&Test::output, &t, placeholders::_1, placeholders::_2);// 绑定类成员变量(公共)function<int&(void)> f2 = bind(&Test::m_number, &t);// 调用f1(520, 1314);f2() = 2333;cout << "t.m_number: " << t.m_number << endl;return 0;
}

在用绑定器绑定类成员函数或者成员变量的时候需要将它们所属的实例对象一并传递到绑定器函数内部。f1的类型是function<void(int, int)>,通过使用std::bind将Test的成员函数output的地址和对象t绑定,并转化为一个仿函数并存储到对象f1中。

使用绑定器绑定的类成员变量m_number得到的仿函数被存储到了类型为function<int&(void)>的包装器对象f2中,并且可以在需要的时候修改这个成员。其中int是绑定的类成员的类型,并且允许修改绑定的变量,因此需要指定为变量的引用,由于没有参数因此参数列表指定为void。

示例程序中是使用 function 包装器保存了 bind 返回的仿函数,如果不知道包装器的模板类型如何指定,可以直接使用 auto 进行类型的自动推导,这样使用起来会更容易一些。

参考:C++ prime plus 第六版

爱编程的大丙

C++——包装器std::function与绑定器std::bind相关推荐

  1. C++11新特性之可调用对象包装器和绑定器

    一.可调用对象 C++中可调用对象有六种. 1.普通函数 普通函数类型可以声明函数,定义函数指针和引用,但是不能定义函数的实体. using Fun = void (int, const string ...

  2. std::function简介

    1. 简介 std::function是一个函数对象的包装器,std::function的实例可以存储,复制和调用任何可调用的目标,包括: 函数. lamada表达式. 绑定表达式或其他函数对象. 指 ...

  3. C++中std::function和std::bind

    1.可调用对象 可调用对象有一下几种定义: 是一个函数指针,参考 C++ 函数指针和函数类型: 是一个具有operator()成员函数的类的对象: 可被转换成函数指针的类对象: 一个类成员函数指针: ...

  4. c++11 std::bind与std::function

     function模板类和bind模板函数,使用它们可以实现类似函数指针的功能,但却比函数指针更加灵活,特别是函数指向类的非静态成员函数时. std::function可以绑定到全局函数/类静态成 ...

  5. std::function用法详解

    std::function用法详解 代码在:VCCommon/functionDemo std::function 简介 类模板 std :: function 是一个通用的多态函数包装器. std ...

  6. c++ std::function

    文章目录 1. 可调用对象 实例 2.可调用对象包装器 std::function 实例 1.包装普通函数 2.包装模板函数 3.包装lambda表达式 4. 包装函数对象 非模板类型 模板类型: 5 ...

  7. C++11 std::function, std::bind, std::ref, std::cref

    C++11 std::function, std::bind, std::ref, std::cref 转自:http://www.jellythink.com/ std::function 看看这段 ...

  8. C++11: std::function<void()> func

    std::function介绍 类模版std::function是一种通用.多态的函数封装.std::function的实例可以对任何可以调用的目标实体进行存储.复制.和调用操作,这些目标实体包括普通 ...

  9. C++11中的std::function

    文章转载自:http://www.jellythink.com/archives/771 看看这段代码 先来看看下面这两行代码: std::function<void(EventKeyboard ...

最新文章

  1. java ipfs文件存储_原来IPFS是这样存储文件的
  2. 【学习总结】之 3Blue1Brown系列
  3. 论CSS样式的使用以及常用属性
  4. 博客园上看到边边的时钟,觉得很好看!推荐
  5. 国外JAVA与IT技术网站地址
  6. java方法不可覆盖_详解Java构造方法为什么不能覆盖,我的钻牛角尖病又犯了.......
  7. web系统数据库服务器,Web Gateway
  8. 华为海外版操作系统曝光?HUAWEI ARK OS现身
  9. Win10系统,开机后提示Desktp不可用的故障解决方法。
  10. 贺利坚老师汇编课程28笔记:在代码段使用数据加个标号start
  11. arttemplate模板引擎里面多层循环
  12. mac的一些使用事项
  13. 链路聚合的介绍以及配置
  14. Elasticsearch-查询性能调优
  15. Microsoft软件保护平台服务一直占用大量CPU资源
  16. reference to ‘count’ is ambiguous报错解决以及C++11中auto自动变量
  17. 基于YOLOV5的自动瞄准(附代码)
  18. 计算机基础知识是一级吗,计算机二级和初级的区别 初级是一级吗
  19. 基于Skeleton的手势识别:SAM-SLR-v2
  20. 水下声呐定位声线跟踪算法

热门文章

  1. 在线作图|如何画韦恩图(包含upset图)
  2. 抄代码的时候总是遇到原始数据应该长什么样的问题??
  3. Microbiome:芝麻菜中肠杆菌科主导核心微生物组并贡献抗生素抗性组
  4. 作物驯化与人类的生活
  5. Plant J:细菌挥发性物质和光合信号激活低铁响应途径
  6. R,Git和Github(上)
  7. R语言plotly可视化:plotly可视化互相重叠的归一化直方图并在直方图中添加密度曲线kde、设置不同的直方图使用不同的分箱大小(bin size)、在直方图的边缘添加边缘轴须图
  8. R语言ggplot2可视化:将条形图(bar plot)和线图(line plot)组合在一起并使用双Y轴(double y axis)进行可视化、其中一个Y轴显示为百分比
  9. R语言可视化R原生plot函数与ggplot可视化结果组合、整合输出实战:原生R绘图plot函数可视化、ggplot2包可视化、R原生plot函数与ggplot可视化结果组合
  10. 数据治理(Data Governance)