深入应用C++11 笔记 (三)
第一章 程序简洁之道
1.5 std:function
可调用对象
- 是一个函数指针
- 是一个具有operator()成员函数的类对象(仿函数)
- 是一个可被转换为函数指针的类对象
- 是一个类成员(函数)指针
void func(void) { //... } struct Foo { void operator()(void) { //... } }; struct Bar { using fr_t = void(*)(void); static void func(void) { //... } operator fr_t(void) { return func; } }; struct A { int a_; void mem_func(void) { //... } }; int main(void) { void(*func_ptr)(void) = &func;//1. 函数指针 func_ptr(); Foo foo;//2. 仿函数 foo(); Bar bar;//3. 可被转换为函数指针的类对象 bar(); //4. 类成员函数指针 void (A::*mem_func_ptr)(void) = &A::mem_func; //或者是类成员指针 int A::*mem_obj_ptr = &A::a_; A aa; (aa.*mem_func_ptr)(); aa.*mem_obj_ptr = 123; return 0; }
可调用对象包装器—std::function
//头文件 functional //std::function void func(void) { std::cout << __FUNCTION__ << std::endl; }; class Foo { public: static int foo_func(int a) { std::cout << __FUNCTION__ << "(" << a << ")->:"; return a; }; }; class Bar { public: int operator()(int a) { std::cout << __FUNCTION__ << "(" << a << ")->:"; return a; }; }; int main(void) { std::function<void(void)> fr1 = func;//绑定一个普通函数 fr1(); //绑定一个类的静态成员函数 std::function<int(int)> fr2 = Foo::foo_func; std::cout << fr2(123) << std::endl; Bar bar; fr2 = bar;//绑定一个仿函数 std::cout << fr2(123) << std::endl; return 0; } //func //Foo::foo_func(123)->:123 //Bar::operator ()(123)->:123
std::function 作为回调函数的示例
class A { std::function<void()> callback_; public: A(const std::function<void()> &f) :callback_(f) {} void notify(void) { callback_();//回调到上层 } }; class Foo { public: void operator()(void) { std::cout << __FUNCTION__ << std::endl; } }; int main(void) { Foo foo; A aa(foo); aa.notify(); return 0; } //Foo::operator ()
从上面可以看出std::function可以取代函数指针的作用。因为它可以保存函数延迟执行,所以比较适合作为回调函数。
std::function还可以作为函数入参
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 << " "; } int main(void) { for (int i = 0; i < 10; ++i) { call_when_even(i, output); } std::cout << std::endl; return 0; } //0 2 4 6 8
std::bind 绑定器
std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。
std::bind作用:
- 将可调用对象与其参数一起绑定成为一个仿函数
- 将多元(参数个数为n,n>1)可调用对象转成一元或者(n-1)元可调用对象,既只绑定部分参数
#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::placeholders::_1是一个占位符,代表这个位置将在函数调用时,被传入的第一个参数所代替
void output(int x, int y) { std::cout << x << " " << y << std::endl; } int main(void) { //对std::bind的返回结果直接施以调用 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::placeholders,来决定空位参数将会属于调用发生时的第几个参数。
std::bind和std::function配合使用代码示例:
//std::bind和std::function配合使用代码示例: class A { public: int i_ = 0; void output(int x, int y) { std::cout << x << " " << y << std::endl; } }; int main(void) { A a; //将output的指针和a绑定,并转换为一个仿函数放入fr中存储 std::function<void(int, int)> fr = std::bind(&A::output, &a, std::placeholders::_1, std::placeholders::_2); fr(1, 2);//1 2 //std::bind将A的成员i_的指针和a绑定,返回的结果被放入fr_i中存储,并可以修改 std::function<int&(void)> fr_i = std::bind(&A::i_, &a); fr_i() = 123; std::cout << a.i_ << std::endl;//123 return 0; }
使用bind简化和增强bind1st和bind2nd
bind1st 和 bind2nd 的作用是将一个二元算子转换为一个一元算子,代码如下:
//查找元素值大于10的元素个数 int count=std::count_if(coll.begin(),coll.end(),std::bind1st(std::less<int>(),10)); //查找元素小于10的元素 int count=std::count_if(coll.begin(),coll.end(),std::bind2nd(std::less<int>(),10));
使用bind的方法,就可以用如下统一的方式去实现,这样就不用关心bind1st还是bind2nd:
using std::placeholders::_1; //查找元素值大于10的元素个数 int count = std::count_if(coll.begin(), coll.end(), std::bind(std::less<int>(), 10, _1)); //less 第一个参数小于第二个参数,则返回true //查找元素小于10的元素 int count = std::count_if(coll.begin(), coll.end(), std::bind(std::less<int>(), _1, 10));
使用组合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>(),std::placeholders::_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>(),std::placeholders::_1,10)); int count=std::count_if(coll.begin(),coll.end(),f);