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 <iostream>
#include <functional>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(111) << std::endl;//绑定一个仿函数
    Bar bar;fr2 = bar;std::cout << fr2(111) << std::endl;return 0;
}

  执行结果:

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

#include <iostream>
#include <functional>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;
}

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

#include <iostream>
#include <functional>void call_when_event(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_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 <iostream>
#include <functional>void call_when_event(int x, const std::function<void(int)>& 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 <iostream>
#include <functional>void output(int x, int y)
{std::cout << x << "  " << y << std::endl;
}int main(void)
{std::bind(output, 1, 2)();                                                 //输出:1 2std::bind(output, std::placeholders::_1, 2)(1);                         //输出:1 2std::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 2std::bind(output, std::placeholders::_2, std::placeholders::_1)(1,2);     //输出:2 1       return 0;
}

3.3 std::bind+std::function

  我们先看一组例子:

#include <iostream>
#include <functional>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<void(int, int)> fr = std::bind(&A::output, &a, std::placeholders::_1, std::placeholders::_2);fr(1, 2);   std::function<int &(void)> 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 <iostream>
#include <functional>int main()
{std::vector<int> 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<int>(), 10));cout << res << endl;// 查找元素值小于10的元素的个数// 也就是使得elem < 10成立的元素个数res = count_if(coll.begin(), coll.end(), std::bind2nd(less<int>(), 10));cout << res << endl;bool b = less<int>(10, 20); // 返回truereturn 0;
}

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

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

3.5 组合使用

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

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

std::bind(std::greater<int>(), std::placeholders::_1, 5);

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

std::bind(std::less_equal<int>(), std::placeholders::_1, 10);

  然后组合,即可调用:

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

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

C11简洁之道:函数绑定相关推荐

  1. C11简洁之道:tupe元祖

    tuple元组是一个固定大小不同类型的值的集合,是泛化的std::pair.我们也可以把它当作一个通用的结构体来使用,不需要创建结构体有获取结构体特征,在某些情况可以取代结构体,使程序更简洁.直观. ...

  2. PHP 代码简洁之道 ( PHP Clean Code)(第二部分)

    PHP 代码简洁之道 ( PHP Clean Code)(第一部分)​ 使用默认参数而不是使用短路运算或者是条件判断 不好的做法: 这是不太好的因为 $breweryName 可以是 NULL. fu ...

  3. 学会这样写代码,一看就是资深工程师,代码简洁之道PHP版本

    文章目录 一.前言 二.规范 2.1 整体结构规范 2.1.1 类的括号前括号单独一行 2.1.2 方法的前括号单独一行 2.1.3 方法内部语句前括号不换行 2.2 变量与常量 2.2.1 变量的命 ...

  4. 2018-1-11-【转】PHP简洁之道

    layout title date author desc in_head post PHP简洁之道 2018-01-11 13:00:02 +0800 南丞 严格的代码规范有助于写出优质的代码 &l ...

  5. js 中的this,默认绑定、隐式绑定、显式绑定、new绑定、箭头函数绑定详解

    壹 ❀ 引 工具猴-免费在线工具-在线工具箱- 可以说this与闭包.原型链一样,属于JavaScript开发中老生常谈的问题了,百度一搜,this相关的文章铺天盖地.可开发好几年,被几道this题安 ...

  6. 五种绑定彻底弄懂this,默认绑定、隐式绑定、显式绑定、new绑定、箭头函数绑定详解

    壹 ❀ 引 可以说this与闭包.原型链一样,属于JavaScript开发中老生常谈的问题了,但开发好几年,依然被几道this指向题安排明明白白的人应该不在少数.对于初学者而言,this概念抽象,变化 ...

  7. R语言中使用pkgbuild::find_rtools查看是否有Rtools、使用Sys.which函数查看make是否存在、如果没有则安装、使用writeLines函数绑定R和Rtools

    R语言中使用pkgbuild::find_rtools(debug = TRUE)查看是否有Rtools.使用Sys.which函数查看make是否存在.如果没有则安装Rtools.使用writeLi ...

  8. boost::typeof模块实现函数绑定的测试程序

    boost::typeof模块实现函数绑定的测试程序 实现功能 C++实现代码 实现功能 boost::typeof模块实现函数绑定的测试程序 C++实现代码 #include <boost/t ...

  9. boost::fusion模块实现一个函数绑定器的测试程序

    boost::fusion模块实现一个函数绑定器的测试程序 实现功能 C++实现代码 实现功能 boost::fusion模块实现一个函数绑定器的测试程序 C++实现代码 #include <b ...

  10. 06 事件处理函数绑定与事件对象

    事件处理函数绑定 DOM事件处理 addEventListener or onclick = function(){} 纯小写 React元素也采用了类似DOM0标准中的事件属性定义的方法 小驼峰 J ...

最新文章

  1. 使用SSMS操作数据-sql
  2. react ajax 分页,React实现分页效果
  3. oracle插入性能优化,Oracle-insert性能优化
  4. html5把六张图片做成立方体,HTML5绘制在立方体上的几何曲线图形
  5. build tut framework on ubuntu
  6. 学习Java 第 4天 流程控制语句 one大白(●—●)
  7. poj 1657 Distance on Chessboard
  8. 多旋翼智能飞行和视觉识别(H题)(组委会自命题)
  9. 分享一个开源的流程图绘制软件--Diagram Designer
  10. KW代码检查规则-RLK.JNDI
  11. 预览文件乱码解决方法记录
  12. Python中的文本替换
  13. 创建 DBLink 的方法
  14. 腾讯公司移动互联网的运营与营销
  15. 网络爬虫爬取拉勾招聘网
  16. 程序员一般通过什么途径接私活?怎样做兼职程序员接私活?
  17. 【uniApp 个推推送功能】
  18. 总线/通信笔记3 —— PDO与SDO的区别
  19. 公共行业短信平台的费用问题
  20. 最新爱K易支付系统源码V2.0版更新

热门文章

  1. 鸿蒙生态发布会,新日XC3亮相华为鸿蒙生态大会,这场合作值得期待!
  2. 原生Django常用API 参数
  3. docker安装mysql并配置文件_Docker安装mysql,并自定义my.cnf
  4. Linux命令 查看端口占用情况
  5. 关于《统计学习方法》中第三章构造kd树算法中的中位数问题
  6. fv-15php1c 安装图,SQLite - C/C++接口 API(一)
  7. java计数器占位符_深入理解Java虚拟机- 学习笔记 - Java内存区域
  8. DevOps使用教程 华为云(9)代码检查
  9. SSL证书 和 CA证书 区别
  10. java中arraylist扩容问题_Arraylist扩容机制