std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。
通俗来讲,它主要有两大作用:
1)将可调用对象与其参数一起绑定成一个仿函数。
2)将多元(参数个数为n,n>1)可调用对象转成一元或者(n-1)元可调用对象,即只绑定部分参数。
下面来看看它的实际使用,如代码清单1-20所示。
代码清单1-20 std::bind的基本用法
#include <iostream> // std::cout #include <functional> // std::bind void call_when_even(int x, const std::function<void(int)>& f) { if (!(x & 1)) // x % 2 == 0 { f(x); } } void output(int x) { std::cout << x << " "; } void output_add_2(int x) { std::cout << x + 2 << " "; } int main(void) { { auto fr = std::bind(output, std::placeholders::_1); for(int i = 0; i < 10; ++i) { call_when_even(i, fr); } std::cout << std::endl; } { auto fr = std::bind(output_add_2, std::placeholders::_1); for(int i = 0; i < 10; ++i) { call_when_even(i, fr); } std::cout << std::endl; } return 0; } 输出结果如下: 0 2 4 6 8 2 4 6 8 10
同样还是上面std::function中最后的一个例子,只是在这里,我们使用了std::bind,在函数外部通过绑定不同的函数,控制了最后的执行结果。
我们使用auto fr保存std::bind的返回结果,是因为我们并不关心std::bind真正的返回类型(实际上std::bind的返回类型是一个stl内部定义的仿函数类型),只需要知道它是一个仿函数,可以直接赋值给一个std::function。当然,这里直接使用std::function类型来保存std::bind的返回值也是可以的。
std::placeholders::_1是一个占位符,代表这个位置将在函数调用时,被传入的第一个参数所替代。
因为有了占位符的概念,std::bind的使用非常灵活,如代码清单1-21所示。
代码清单1-21 std::bind的占位符
#include <iostream> // std::cout #include <functional> // std::bind void output(int x, int y) { std::cout << x << " " << y << std::endl; } int main(void) { std::bind(output, 1, 2)(); // 输出: 1 2 std::bind(output, std::placeholders::_1, 2)(1); // 输出: 1 2 std::bind(output, 2, std::placeholders::_1)(1); // 输出: 2 1 // error: 调用时没有第二个参数 std::bind(output, 2, std::placeholders::_2)(1); std::bind(output, 2, std::placeholders::_2)(1, 2); // 输出: 2 2 // 调用时的第一个参数被吞掉了 std::bind(output, std::placeholders::_1, std::placeholders::_2)(1, 2); // 输出: 1 2 std::bind(output, std::placeholders::_2, std::placeholders::_1)(1, 2); // 输出: 2 1 return 0; }
上面对std::bind的返回结果直接施以调用。可以看到,std::bind可以直接绑定函数的所有参数,也可以仅绑定部分参数。
在绑定部分参数的时候,通过使用std::placeholders,来决定空位参数将会属于调用发生时的第几个参数。
下面再来看一个例子,如代码清单1-22所示。
代码清单1-22 std::bind和std::function配合使用
#include <iostream> #include <functional> class A { public: int i_ = 0; void output(int x, int y) { std::cout << x << " " << y << std::endl; } }; int main(void) { A a; std::function<void(int, int)> fr = std::bind(&A::output, &a, std::placeholders::_1 , std::placeholders::_2); fr(1, 2); // 输出: 1 2 std::function<int&(void)> fr_i =std::bind(&A::i_, &a); fr_i() = 123; std::cout << a.i_ << std::endl; // 输出: 123 return 0; }
fr的类型是std::function<void(int, int)>。我们通过使用std::bind,将A的成员函数output的指针和a绑定,并转换为一个仿函数放入fr中存储。
之后,std::bind将A的成员i_的指针和a绑定,返回的结果被放入std::function<int&(void)>中存储,并可以在需要时修改访问这个成员。
现在,通过std::function和std::bind的配合,所有的可调用对象均有了统一的操作方法。
下面再来看几个std::bind的使用例子。
1.使用bind简化和增强bind1st和bind2nd
其实bind简化和增强了之前标准库中bind1st和bind2nd,它完全可以替代bind1s和bind2st,并且能组合函数。我们知道,bind1st和bind2nd的作用是将一个二元算子转换成一个一元算子,代码如下:
// 查找元素值大于10的元素的个数 int count = std::count_if(coll.begin(), coll.end(), std::bind1st(less<int>(), 10)); // 查找元素之小于10的元素 int count = std::count_if(coll.begin(), coll.end(), std::bind2nd(less<int>(), 10));
本质上是对一个二元函数less<int>的调用,但是它却要分别用bind1st和bind2nd,并且还要想想到底是用bind1st还是bind2nd,用起来十分不便。
现在我们有了bind,就可以以统一的方式去实现了,代码如下:
using std::placeholders::_1; // 查找元素值大于10的元素的个数 int count = std::count_if(coll.begin(), coll.end(),std::bind(less<int>(), 10, _1)); // 查找元素之小于10的元素 int count = std::count_if(coll.begin(), coll.end(),std::bind(less<int>(), _1, 10));
这样就不用关心到底是用bind1st还是bind2nd,只需要使用bind即可。
2.使用组合bind函数
bind还有一个强大之处就是可以组合多个函数。假设要找出集合中大于5小于10的元素个数应该怎么做呢?
首先,需要一个用来判断是否大于5的功能闭包,代码如下:
std::bind(std::greater<int>(), std::placeholders::_1, 5);
这里std::bind返回的仿函数只有一个int参数。当输入了这个int参数后,输入的int值将直接和5进行大小比较,并在大于5时返回true。
然后,我们需要一个判断是否小于10的功能闭包:
std::bind(std::less_equal<int>(),std::placeholders::_1,10);
有了这两个闭包之后,只需要用逻辑与把它们连起来:
using std::placeholders::_1; std::bind(std::logical_and<bool>(), std::bind(std::greater<int>(), _1, 5), std::bind(std::less_equal<int>(), _1, 10)); 然后就可以复合多个函数(或者说闭包)的功能: using std::placeholders::_1; // 查找集合中大于5小于10的元素个数 auto f = std::bind(std::logical_and<bool>(), std::bind(std::greater<int>(), _1, 5), std::bind(std::less_equal<int>(), _1, 10)); int count = std::count_if(coll.begin(), coll.end(), f);