博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C11简洁之道:函数绑定
阅读量:4326 次
发布时间:2019-06-06

本文共 7077 字,大约阅读时间需要 23 分钟。

1、  可调用对象

  在C++中,有“可调用对象”这么个概念,那么什么是调用对象呢?有哪些情况?我们来看看:

  • 函数指针;
  • 具有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 mia;    void mem_func(void)    {           //...    }};int main(void){    //函数指针    void(* func_ptr)(void) = &func;    func_ptr();    //仿函数    Foo foo;    foo();    //被转为指针的类对象    Bar bar;    bar();    //类成员函数指针    void (A::*mem_func_ptr)(void) = &A::mem_func;    //类成员指针    int A::*mem_obj_ptr = &A::mia;    A aa;    (aa.*mem_func_ptr)();    aa.*mem_obj_ptr = 123;    return 0;}

  上述的对象都是可调用对象,这些对象的类型统称为“可调用类型”。这些可调用对象都具有统一的操作形式,除了类成员指针之外,都是通过括号的方式来进行调用,但是定义的方法比较多,在C++11中增加了std::function来进行函数对象的调用。

2、  std::function

  std::function是一个可调用对象的包装器,他是一个类模板,可以容纳除了类成员(函数)指针之外的所用可调用对象,通过指定他的模板参数,可以以统一的方式处理函数、函数对象、函数指针,并允许保存或者延迟执行。

  当我们给std::function填入合适的函数签名(即一个函数类型,只需要包括返回值和参数列表)之后,它就变成了一个可以容纳所有这一类调用方式的“函数包装器”。

#include 
#include
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
fr1 = func; fr1(); //绑定一个静态成员函数 std::function
fr2 = Foo::foo_func; std::cout << fr2(111) << std::endl; //绑定一个仿函数 Bar bar; fr2 = bar; std::cout << fr2(111) << std::endl; return 0;}

  执行结果:

 

  std::function还可以取代函数指针的作用,因为它可以保存函数延迟执行,所以也适合做回调函数。

#include 
#include
class A{ std::function
callback;public: A(const std::function
&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;}

  std::function还可以作为函数入参,比普通函数指针更加灵活和便利。

#include 
#include
void call_when_event(int x, const std::function
& 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_event(i, output); } std::cout << std::endl; return 0;}

3、  std::bind绑定器

3.1 std::bind绑定器

  std::bind用来将可调用对象与起参数一起进行绑定,绑定的结果使用std::function进行保存,并在我们需要调用的时候调用。它主要有两大作用:

  • 将可调用对象和参数绑定成为一个仿函数;
  • 将多元(参数个数为n,n-1)可调用对象转换成一元或者(n-1)元可调用对象,即只绑定部分对象。

  我们来看实际使用:

#include 
#include
void call_when_event(int x, const std::function
& f){ if(!(x & 1)) //x % 2 == 0 { f(x); }}void output(int x){ std::cout << x << " ";}void output2(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_event(i, fr); } std::cout << std::endl; } { auto fr = std::bind(output2, std::placeholders::_1); for(int i = 0; i < 10; i++) { call_when_event(i, fr); } std::cout << std::endl; } return 0;}

  通过代码我们可以知道std::bind在函数外部通过绑定不同的函数,控制执行结果。这里我们还使用了std::placeholders占位符来绑定函数参数。

3.2 std::placeholders

  通过std::placeholders占位符绑定函数参数,使得std::bind的使用非常灵活。std::placeholders决定函数占用位置取用输入参数的第几个参数。

#include 
#include
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 //std::bind(output, 2, std::placeholders::_2)(1); //error,没有第二个参数 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;}

3.3 std::bind+std::function

  我们先看一组例子:

#include 
#include
class A{public: int mi = 0; void output(int x, int y) { std::cout << x << " " << y << std::endl; }};int main(void){ A a; std::function
fr = std::bind(&A::output, &a, std::placeholders::_1, std::placeholders::_2); fr(1, 2); std::function
fr_i = std::bind(&A::mi, &a); fr_i() = 123; std::cout << a.mi << std::endl; return 0;}

  fr的类型是std::function<void(int, int)>,我们通过std::bind将A的成员函数output的指针和a绑定,并转换为一个仿函数存储在fr中。

  通过std::bind将A的成员mi的指针和a绑定,返回的结果放入类型为std::function<int &(void)>的fr_i中,可以在需要的时候修改这个成员的值。

3.4 改善标准函数

  假如我们有一个这样的需求,对某个集合里面的元素进行统计,假设元素类型为int,那么我们需要对类型做比较,必须有一个阀值,即大于或者小于这个数。这里我们可以通过标准库的函数来实现。

#include 
#include
int main(){ std::vector
coll; for (int i = 1; i <= 10; ++i) { coll.push_back(i); } // 查找元素值大于10的元素的个数 // 也就是使得10 < elem成立的元素个数 int res = count_if(coll.begin(), coll.end(), std::bind1st(less
(), 10)); cout << res << endl; // 查找元素值小于10的元素的个数 // 也就是使得elem < 10成立的元素个数 res = count_if(coll.begin(), coll.end(), std::bind2nd(less
(), 10)); cout << res << endl; bool b = less
(10, 20); // 返回true return 0;}

  本质上是对一个二元函数less<int>的调用,但是要分别调用bind1st,bind2nd,用起来比较繁杂,现在我们有bind,可以用统一的方式去实现。并不用关心是bind1st还是bind2nd,用bind即可。

#include 
#include
int main(){ using std::placeholders::_1; std::vector
coll; //查找元素值大于10的元素个数 int count = std::count_if(coll.begin(), coll.end(), std::bind(less
(), 10, _1)); //查找元素值小于10的元素个数 count = std::count_if(coll.begin(), coll.end(), std::bind(less
(), _1, 10)); return 0;}

3.5 组合使用

  bind可以绑定多个函数,假设我们要对某个集合在大于5小于10的元素个数进行统计,我们该怎么封装呢?

  首先封装一个判断是否大于5的函数,使其输入只有一个参数,直接和5比较,大于5返回true。

std::bind(std::greater
(), std::placeholders::_1, 5);

  同样,我们需要封装一个判断是否小于10的函数,使其输入一个参数,小于10则返回true。

std::bind(std::less_equal
(), std::placeholders::_1, 10);

  然后组合,即可调用:

using std::placeholders::_1;auto f = std::bind(std::logical_and
(),std::bind(std::greater
(), std::placeholders::_1, 5),std::bind(std::less_equal
(), std::placeholders::_1, 10));int count = std::count_if(coll.begin(), coll.end(), f);

转载于:https://www.cnblogs.com/ChinaHook/p/7658411.html

你可能感兴趣的文章
Linux下Nginx安装
查看>>
LVM扩容之xfs文件系统
查看>>
Hbase记录-client访问zookeeper大量断开以及参数调优分析(转载)
查看>>
代码片段收集
查看>>
vue-cli3创建项目时报错
查看>>
输入1-53周,输出1-53周的开始时间和结束时间
查看>>
实验二
查看>>
shell——按指定列排序
查看>>
crash 收集
查看>>
507 LOJ 「LibreOJ NOI Round #1」接竹竿
查看>>
UI基础--烟花动画
查看>>
2018. 2.4 Java中集合嵌套集合的练习
查看>>
精通ASP.NET Web程序测试
查看>>
vue 根据不同属性 设置背景
查看>>
51Nod1601 完全图的最小生成树计数 Trie Prufer编码
查看>>
Codeforces 1110D. Jongmah 动态规划
查看>>
android驱动在win10系统上安装的心酸历程
查看>>
优雅的程序员
查看>>
oracle之三 自动任务调度
查看>>
Android dex分包方案
查看>>