6、绑定器
一、作用
std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。通俗来讲,它主要有两大作用:
(1)将可调用对象与其参数一起绑定成一个仿函数。
(2)将多元(参数个数为n,n>1)可调用对象转换为一元或者(n-1)元可调用对象,即只绑定部分参数。
二、语法格式
// 绑定非类成员函数/变量 auto f = std::bind(可调用对象地址, 绑定的参数/占位符); // 绑定类成员函/变量 auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);
三、
范例1
#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; }
测试代码输出的结果:
0 2 4 6 8 10 12 14 16 18
在上面的程序中,使用了 std::bind 绑定器,在函数外部通过绑定不同的函数,控制了最后执行的结果。std::bind绑定器返回的是一个仿函数类型,得到的返回值可以直接赋值给一个std::function,在使用的时候我们并不需要关心绑定器的返回值类型,使用auto进行自动类型推导就可以了。
placeholders::_1 是一个占位符,代表这个位置将在函数调用时被传入的第一个参数所替代。同样还有其他的占位符 placeholders::_2、placeholders::_3、placeholders::_4、placeholders::_5 等……
有了占位符的概念之后,使得 std::bind 的使用变得非常灵活:
四、
范例2:使用占位符
#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 的配合之后,就可以完美的解决这个问题了,再来看一个例子,然后再解释里边的细节:
五、
范例3:绑定类成员函数
#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 进行类型的自动推导,这样使用起来会更容易一些。