目录
第一节:std::function
第二节:std::bind
2-1.基本介绍
2-2.调整顺序(不常用)
2-3.调整个数
2-4.std::bind与std::function
下期预告:
C++中有3种可调用对象:函数指针、仿函数对象、lambda函数,经过包装器包装后屏蔽了底层细节,就可以用更加通用的方式使用3种可调用函数了。
第一节:std::function
std::function的底层实现其实是仿函数对象,通过嵌套一层对象,实现了3种可调用对象在类型上的统一(都变成了对象)。
std::function包含在头文件<functional>中,它的使用方法如下:
std::function<返回类型(参数类型)> 名字 = 可调用对象;
#include <functional>// 包装函数指针 int Add(int a,int b) {return a + b; } std::function<int(int, int)> f1 = Add;// 包装仿函数 class Func { public:int operator()(int a, int b){return a + b;} }; std::function<int(int, int)> f2 = Func();// 包装lambda函数 std::function<int(int, int)> f3 = [](int a, int b) {return a + b; };int main() {std::cout << f1(1, 2) << std::endl;std::cout << f2(1, 2) << std::endl;std::cout << f3(1, 2) << std::endl;return 0; }
![]()
如果封装的是类中的非静态成员函数,参数记得加上隐含的this指针类型,而且只有公有函数才能被封装:
class Person { public:void print(){std::cout << "我被调用了!" << std::endl;} }; std::function<void(Person*)> f = &Person::print; // 这个&必不可少
调用时也要传入一个对象的指针:
int main() {Person LiHua;f(&LiHua); // 等价于 LiHua.print();return 0; }
若包装时使用Person作为参数,而不是指针,就可以直接传入对象进行调用:
#include <functional> class Person { public:void print(){std::cout << "我被调用了!" << std::endl;} }; std::function<void(Person)> f = &Person::print; // 这个&必不可少 int main() {Person p;f(p);return 0; }
它们本质上都是使用对象调用成员函数。
如果是类中的公有静态函数,则不需要隐含的this指针类型:
#include <functional> class Person { public:static void print(){std::cout << "我被调用了!" << std::endl;} }; std::function<void()> f = &Person::print; // 这个&可以省略 int main() {f(); // 等价于 LiHua.print();return 0; }
第二节:std::bind
使用bind可以调整函数参数的传入顺序和个数。
2-1.基本介绍
std::bind的原型如下:
template<class Fn,class ...Args> bind(Fn&& fn,Args&&... args);template<class Ret,class Fn,class ...Args> bind(Fn&& fn,Args&&... args);
模版参数包就包含了可调用对象的所有参数类型。
std::bind的使用格式如下:
std::bind(可调用对象,占位符1,占位符2,...);
占位符有多个,而且占位符与参数是一一对应的,占位符1表示第一个参数,占位符2表示第二个参数。
2-2.调整顺序(不常用)
因为占位符1表示第一个参数,占位符2表示第二个参数,只要把占位符1放在后面,那么第一个参数就需要在后面传入了:
#include <functional> void func(int a, float b, std::string c) {std::cout << a << std::endl;std::cout << b << std::endl;std::cout << c << std::endl; } int main() {auto f = std::bind(&func,std::placeholders::_3, std::placeholders::_2, std::placeholders::_1);return 0; }
像这样,它的占位符顺序是3、2、1,而不是正常的1、2、3。那么使用 f 时参数类型应该先传入std::string,再传入float,最后传入int:
f("Hello", 0.2, 12);
只是顺序改变了,对原函数的逻辑没有影响。
2-3.调整个数
调整个数不是真正的减少参数个数,实际上是指定某个或者某些参数的值,调用时这些参数就赋值为指定的值。
指定后,剩下的参数再重新分占位符1、2、3:
#include <functional> void func(int a, float b, std::string c) {std::cout << a << std::endl;std::cout << b << std::endl;std::cout << c << std::endl; } int main() {// 指定第一个参数的值为12auto f = std::bind(&func,12, std::placeholders::_1, std::placeholders::_2);// 第一个参数已经预先传好了,只传剩下两个参数f(0.2,"Hello");return 0; }
这种用法的主要场景是在类中进行函数传参时,如果一个函数类型的参数只接收3个参数的可调用对象,而对象中的非静态函数有3个显式的参数+1个this指针,并且这个函数因为需要用到对象的数据而不能设置为静态,也不能减少参数。这时候将 this 作为指定为 this指针 的值,它就变成只用传入3个参数的可调用对象了。
例如:
它的第一个参数就被指定为 this。
2-4.std::bind与std::function
可调用对象被std::bind包装过后也是可调用对象,所以可以将std::bind后的可调用对象传给std::function管理起来:
#include <functional> void func(int a, float b, std::string c) {std::cout << a << std::endl;std::cout << b << std::endl;std::cout << c << std::endl; } int main() {// 如果指定了值,那么<>中的参数也省略被指定的类型 std::function<void(float,std::string)> f = std::bind(&func, 12, std::placeholders::_1, std::placeholders::_2);// 第一个参数已经预先传好了,只传剩下两个参数f(0.2,"Hello");return 0; }
下期预告:
下一章将讲述C++11的线程库,包括线程的管理、锁。