简介
C++11新增了std::function和std::bind。用于函数的包装以及参数的绑定。可以替代一些函数指针,回调函数的场景。
std::function
std::function对象包装器
std::function是可调用对象的包装器,它可以用来用统一的方式来处理函数、函数对象、函数指针,并允许保存和延迟执行它们。比较难理解,可以从代码上深入:
#include <iostream> #include <functional> void func(void) { std::cout << __FUNCTION__ << std::endl; } class Foo { public: static int foo_func(int a) { std::cout << __FUNCTION__ << " :input param:" << a << std::endl; return a; } }; class Bar { public: int operator()(int a) { std::cout << __FUNCTION__ << " :input param:" <<a<< std::endl;// return a; } }; int main() { std::function<void(void)> fr1 = func;//绑定普通函数 fr1(); std::function<int(int)> fr2 = Foo::foo_func;//绑定一个类的静态成员函数 std::cout << "result:"<< fr2(123) << std::endl; Bar bar; std::function<int(int)> fr3 = bar;//绑定一个仿函数 //fr2 = bar;//这里用fr2也可以,因为这两个函数的返回值和参数表是一样的 std::cout << "result" <<fr3(123) << std::endl; return 0; }
注意:代码中__FUNCTION__是预定义标识符,基本功能是实现返回所在函数的名字,便于调试日志打印。
运行结果:
可以看出使用给std::function赋值上对应的函数返回值和函数参数表,它就可以容纳这一类调用方式的函数,被称为“函数包装器”。如上述的fr2可以容纳Foo::foo_func和bar。
这里可以看出function类似于函数指针的作用,可以保存各种类型的函数地址。
std::function做回调函数
#include <iostream> #include <functional> class A { std::function<void(int)> callback_; public: A(const std::function<void(int)>& f) : callback_(f) {} void notify(int a) { callback_(a); } }; class Foo { public: void operator()(int a) { std::cout << __FUNCTION__ <<" a:" << a << std::endl; } }; int main() { Foo foo; A aa(foo); aa.notify(111); return 0; }
这里可以看出function可以取代函数指针的作用,可以用function保存函数延迟执行,所有比较适合用在回调函数场景。
std::bind绑定器
std::bind可以将可调用对象和其参数一起绑定,绑定后的结果可以用std::function进行保存。
其中绑定普通函数和绑定成员函数的写法有所不同。
bind绑定普通函数
#include <iostream> #include <functional> void input(int x) { std::cout << x << std::endl; } int main() { std::function<void(int)> fr = std::bind(input, std::placeholders::_1); auto fr1 = std::bind(input, std::placeholders::_1);//这里用auto接收也行 fr(2); fr1(3); return 0; }
其中std::placeholders::_1是一个占位符,表示这个位置将在函数调用时,被传入的第一个参数替代。
占位符的使用方法:
#include <iostream> #include <functional> void input(int x, int y) { std::cout << x << " " << y << std::endl; } int main() { std::function<void(int, int)> fr = std::bind(input, std::placeholders::_1, 2);//这里用auto接收也行 fr(4, 5);//4 2 fr = std::bind(input, 2, std::placeholders::_1); fr(4, 5);//2 4 fr = std::bind(input, std::placeholders::_1, std::placeholders::_2); fr(4, 5);//4 5 fr = std::bind(input, std::placeholders::_2, 2); fr(4, 5);//5 2 fr = std::bind(input, 2, std::placeholders::_2); fr(4, 5);//2 5 return 0; }
结果:
在绑定参数时,可以通过占位符std::placeholders来决定空位参数会属于调用发生时的第几个参数。
bind绑定成员函数
bind可以绑定成员函数和成员变量。其中绑定成员函数和绑定普通函数时是有一些差别的。
#include <iostream> #include <functional> class MyClass { public: int i_ = 0; void foo(int a, int b) { std::cout << a << " " << b << std::endl; } }; int main() { MyClass obj; auto boundFunc = std::bind(&MyClass::foo, &obj, std::placeholders::_1, std::placeholders::_2);//绑定成员函数 boundFunc(3, 4); auto fr_i = std::bind(&MyClass::i_, &obj);//绑定成员变量 fr_i() = 123; return 0; }
当使用 std::bind 绑定成员函数时,需要注意以下几点:
- 需要使用成员函数的指针或函数对象来进行绑定。对于指针,需要使用 & 取址符号获取成员函数的地址。
- 需要提供对象的指针(或引用)作为第一个参数,以便在调用时正确地调用成员函数。
可以看到绑定普通函数时是不需要提供对象的指针或引用作为参数。