std::bind()功能学习
转自:https://blog.csdn.net/Jxianxu/article/details/107382049,https://www.jianshu.com/p/af303820fe0f,这个讲的非常好!
1.介绍
// 绑定到普通函数 template< class F, class... Args > bind( F&& f, Args&&... args );
std::bind的头文件是 <functional>,bind作用于函数上(包括普通函数,类成员函数等),返回类型为std::function<R(T....)>函数对象(A function object g of unspecified type T
),这个类里面实现了operator()操作符,使得这个对象能像函数一样能使用()调用。
std::bind返回一个函数对象function类型,主要看传入的函数F的参数和返回类型,参数要么被绑定到值,要么被绑定到placeholders(占位符,如_1, _2, ..., _n)。
主要作用:
- 将可调用对象和其参数绑定成一个仿函数;
- 只绑定部分参数,减少可调用对象传入的参数。
2.绑定普通函数
double callableFunc (double x, double y) {return x/y;} auto NewCallable = std::bind (callableFunc, std::placeholders::_1,2); std::cout << NewCallable (10) << '\n'; // 输出5
- bind的第一个参数是函数名,普通函数做实参时,会隐式转换成函数指针。因此std::bind(callableFunc,_1,2)等价于std::bind (&callableFunc,_1,2);
- _1表示占位符,位于<functional>中,std::placeholders::_1;
- 第一个参数被占位符占用,表示这个参数以调用时传入的参数为准,在这里调用NewCallable时,给它传入了10,其实就想到于调用callableFunc(10,2);
3.绑定类对象的成员函数
class Base { void display_sum(int a1, int a2) { std::cout << a1 + a2 << '\n'; } int m_data = 30; }; int main() { Base base; auto newiFunc = std::bind(&Base::display_sum, &base, 100, std::placeholders::_1); f(20); // should out put 120. }
- bind绑定类成员函数时,第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址。
- 必须显式地指定&Base::diplay_sum,因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在Base::display_sum前添加&;
- 使用对象成员函数的指针时,必须要知道该指针属于哪个对象,因此第二个参数为对象的地址 &base;
// 还可以绑定到类的数据成员,不太了解这是什么用法,之后有用到再说。
4.绑定引用参数
如果函数声明中的形参类型是引用,那么在bind时可以通过ref()函数来绑定参数。
传入bind的参数要加std::ref,会引用传参,可以在bind调用函数时修改参数的内容。
5.语法示例
好处:
- 函数对象操作方式和函数几乎一致,不会带来新的语法难度。
- 函数对象可以预设函数的部分参数,更加灵活。
- 函数对象可以绑定到类的成员函数。
注意,函数对象和函数指针是不一样的,两个格式声明不同:
//函数指针: void (*) (int, const std::string&); //返回类型(*)(参数类型列表) //函数对象: std::function<void(int)> f_int //std::function<返回类型(参数类型列表)>
样例case,很厉害:
#include<iostream> #include<atomic> #include<thread> #include<vector> #include<atomic> #include <functional> using namespace std; // g++ --std=c++11 ./seqid.cpp -o seqid -lpthread class T { public: static bool big_than(int a, int b) { return a > b; } bool mem_big_than(int a, int b) { return a > b; } }; int for_each_array(std::vector<int>& vec, std::function<bool(int)> f) {// 注意到这个函数对象f只有一个int类型的形参 int count = 0; for (auto iter = vec.begin(); iter != vec.end(); ++iter) { if (f(*iter)) ++count; } return count; } int main() { //绑定到类的静态函数 auto f = std::bind(&T::big_than, std::placeholders::_1, 10); std::vector<int> vec = {100,100,300,2,1,3}; int res = for_each_array(vec, f); cout<<res<<endl; //绑定到类的普通成员函数 T t; auto f2 = std::bind(&T::mem_big_than, &t, std::placeholders::_1, 10); res = for_each_array(vec, f2); cout<<res<<endl; return 0; }
case2,绑定类对象作为函数形参:
struct Person{ void pprint(){cout<<"Person.\n";} }; typedef std::function<void(const std::string &name)> callback; void setMyFunc(callback call){ call("hh"); } void preFunc(Person* p,const std::string &name){// 固定类参数到这个类型 p->pprint(); cout<<name<<" ok\n"; } int main() { Person p(5); auto myfunc = std::bind(preFunc,&p,std::placeholders::_1); setMyFunc(myfunc); }
6.应用场景
回调处理函数。某个处理函数中,遇到某种情况,可能需要在另一个类执行某些操作,且这些操作的参数依赖于处理函数中的变量。如下所示:
T *obj = get_obj(); int param = argv[0]; int ret = deal_with_param(param); // 在执行deal_with_param时,发生了错误。需要在类T中做某些通知操作。
// 这个通知的动作依赖于当下的参数param。这里的bind就起到了回调的作用。 if (ret) { obj->failed(std::bind(some_func, std::placeholders::_1, param)); }
7.自己定义function类型
class Base { public: void display_sum(int a1, int a2) { std::cout << a1 + a2 << '\n'; } }; int main() { Base base; // 这样是不对的,因为已经display_sum绑定到两个int了,参数已固定,f是没有形参的了 std::function<void(int, int)> f = std::bind(&Base::display_sum, &base,1,2); // 正确的写法 std::function<void()> f = std::bind(&Base::display_sum, &base,1,2); f();// 输出3 // 或者使用占位符,延迟传参 std::function<void(int)> f2 = std::bind(&Base::display_sum, &base,1,std::placeholders::_1); f2(10); // 输出11 return 0; }