C++ function bind以及lamda表达式

 本文是C++0x系列的第四篇,主要是内容是C++0x中新增的lambda表达式, function对象和bind机制。之所以把这三块放在一起讲,是因为这三块之间有着非常密切的关系,通过对比学习,加深对这部分内容的理解。在开始之间,首先要讲一个概念,closure(闭包),这个概念是理解lambda的基础。下面我们来看看wikipedia上对于计算机领域的closure的定义:

A closure (also lexical closure, function closure or function value) isfunction together witha referencing environment for the non-local variables of that function.

 上面的大义是说,closure是一个函数和它所引用的非本地变量的上下文环境的集合。从定义我们可以得知,closure可以访问在它定义范围之外的变量,也即上面提到的non-local vriables,这就大大增加了它的功力。关于closure的最重要的应用就是回调函数,这也是为什么这里把function, bind和lambda放在一起讲的主要原因,它们三者在使用回调函数的过程中各显神通。下面就为大家一步步接开这三者的神秘面纱。

1. function

 我们知道,在C++中,可调用实体主要包括函数,函数指针,函数引用,可以隐式转换为函数指定的对象,或者实现了opetator()的对象(即C++98中的functor)。C++0x中,新增加了一个std::function对象,std::function对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。我们来看几个关于function对象的例子:

#include < functional> std::function< size_t (const char*) > print_func; /// normal function -> std::function objectsize_t CPrint(const char*) { ... }print_func = CPrint;print_func("hello world"): /// functor -> std::function objectclass CxxPrint{public:    size_t operator()(const char*) { ... }};CxxPrint p;print_func = p;print_func("hello world");

    在上面的例子中,我们把一个普通的函数和一个functor赋值给了一个std::function对象,然后我们通过该对象来调用。其它的C++中的可调用实体都可以像上面一样来使用。通过std::function的包裹,我们可以像传递普通的对象一样来传递可调用实体,这样就很好解决了类型安全的问题。了解了std::function的基本用法,下面我们来看一些使用过程中的注意事项:

  • (1)关于可调用实体转换为std::function对象需要遵守以下两条原则:
    a. 转换后的std::function对象的参数能转换为可调用实体的参数
    b. 可高用实体的返回值能转换为std::function对象的(这里注意,所有的可调用实体的返回值都与返回void的std::function对象的返回值兼容)。
  • (2)std::function对象可以refer to满足(1)中条件的任意可调用实体
  • (3)std::function object最大的用处就是在实现函数回调,使用者需要注意,它不能被用来检查相等或者不相等
2. bind

 bind是这样一种机制,它可以预先把指定可调用实体的某些参数绑定到已有的变量,产生一个新的可调用实体,这种机制在回调函数的使用过程中也颇为有用。C++98中,有两个函数bind1st和bind2nd,它们分别可以用来绑定functor的第一个和第二个参数,它们都是只可以绑定一个参数。各种限制,使得bind1st和bind2nd的可用性大大降低。C++0x中,提供了std::bind,它绑定的参数的个数不受限制,绑定的具体哪些参数也不受限制,由用户指定,这个bind才是真正意义上的绑定,有了它,bind1st和bind2nd就没啥用武之地了,因此C++0x中不推荐使用bind1st和bind2nd了,都是deprecated了。下面我们通过例子,来看看bind的用法:

#include < functional> int Func(int x, int y);auto bf1 = std::bind(Func, 10, std::placeholders::_1);bf1(20); ///< same as Func(10, 20) class A{public:    int Func(int x, int y);}; A a;auto bf2 = std::bind(&A::Func, a, std::placeholders::_1, std::placeholders::_2);bf2(10, 20); ///< same as a.Func(10, 20) std::function< int(int)> bf3 = std::bind(&A::Func, a, std::placeholders::_1, 100);bf3(10); ///< same as a.Func(10, 100)

    上面的例子中,bf1是把一个两个参数普通函数的第一个参数绑定为10,生成了一个新的一个参数的可调用实体体; bf2是把一个类成员函数绑定了类对象,生成了一个像普通函数一样的新的可调用实体; bf3是把类成员函数绑定了类对象和第二个参数,生成了一个新的std::function对象。看懂了上面的例子,下面我们来说说使用bind需要注意的一些事项:

  • (1)bind预先绑定的参数需要传具体的变量或值进去,对于预先绑定的参数,是pass-by-value的
  • (2)对于不事先绑定的参数,需要传std::placeholders进去,从_1开始,依次递增。placeholder是pass-by-reference的
  • (3)bind的返回值是可调用实体,可以直接赋给std::function对象
  • (4)对于绑定的指针、引用类型的参数,使用者需要保证在可调用实体调用之前,这些参数是可用的
  • (5)类的this可以通过对象或者指针来绑定
3. lambda

 讲完了function和bind, 下面我们来看lambda。有python基础的朋友,相信对于lambda不会陌生。看到这里的朋友,请再回忆一下前面讲的closure的概念,lambda就是用来实现closure的东东。它的最大用途也是在回调函数,它和前面讲的function和bind有着千丝万缕的关系。下面我们先通过例子来看看lambda的庐山真面目:

vector< int> vec;/// 1. simple lambdaauto it = std::find_if(vec.begin(), vec.end(), [](int i) { return i > 50; });class A{public:    bool operator(int i) const { return i > 50; }};auto it = std::find_if(vec.begin(), vec.end(), A()); /// 2. lambda return syntaxstd::function< int(int)> square = [](int i) -> int { return i * i; } /// 3. lambda expr: capture of local variable{    int min_val = 10;    int max_val = 1000;     auto it = std::find_if(vec.begin(), vec.end(), [=](int i) {        return i > min_val && i < max_val;         });     auto it = std::find_if(vec.begin(), vec.end(), [&](int i) {        return i > min_val && i < max_val;        });     auto it = std::find_if(vec.begin(), vec.end(), [=, &max_value](int i) {        return i > min_val && i < max_val;        });} /// 4. lambda expr: capture of class memberclass A{public:    void DoSomething()private:    std::vector<int>  m_vec;    int               m_min_val;    int               m_max_va;}; /// 4.1 capture member by thisvoid A::DoSomething(){    auto it = std::find_if(m_vec.begin(), m_vec.end(), [this](int i){        return i > m_min_val && i < m_max_val; });} /// 4.2 capture member by default pass-by-valuevoid A::DoSomething(){    auto it = std::find_if(m_vec.begin(), m_vec.end(), [=](int i){        return i > m_min_val && i < m_max_val; });} /// 4.3 capture member by default pass-by-referencevoid A::DoSomething(){    auto it = std::find_if(m_vec.begin(), m_vec.end(), [&](int i){        return i > m_min_val && i < m_max_val; });}

 上面的例子基本覆盖到了lambda表达的基本用法。我们一个个来分析每个例子(标号与上面代码注释中1,2,3,4一致):

  • (1)这是最简单的lambda表达式,可以认为用了lambda表达式的find_if和下面使用了functor的find_if是等价的
  • (2)这个是有返回值的lambda表达式,返回值的语法如上面所示,通过->写在参数列表的括号后面。返回值在下面的情况下是可以省略的:
    a. 返回值是void的时候
    b. lambda表达式的body中有return expr,且expr的类型与返回值的一样
  • (3)这个是lambda表达式capture本地局部变量的例子,这里三个小例子,分别是capture时不同的语法,第一个小例子中=表示capture的变量pass-by-value, 第二个小拿出中&表示capture的变量pass-by-reference,第三个小例子是说指定了default的pass-by-value, 但是max_value这个单独pass-by-reference
  • (4)这个是lambda表达式capture类成员变量的例子,这里也有三个小例子。第一个小例子是通过this指针来capture成员变量,第二、三个是通过缺省的方式,只不过第二个是通过pass-by-value的方式,第三个是通过pass-by-reference的

分析完了上面的例子,我们来总结一下关于lambda表达式使用时的一些注意事项:

  • (1)lambda表达式要使用引用变量,需要遵守下面的原则:
    a. 在调用上下文中的局部变量,只有capture了才可以引用(如上面的例子3所示)
    b. 非本地局部变量可以直接引用
  • (2)使用者需要注意,closure(lambda表达式生成的可调用实体)引用的变量(主要是指针和引用),在closure调用完成之前,必须保证可用,这一点和上面bind绑定参数之后生成的可调用实体是一致的
  • (3)关于lambda的用处,就是用来生成closure,而closure也是一种可调用实体,所以可以通过std::function对象来保存生成的closure,也可以直接用auto

 通过上面的介绍,我们基本了解了function, bind和lambda的用法,把三者结合起来,C++将会变得非常强大,有点函数式编程的味道了。最后,这里再补充一点,对于用bind来生成function和用lambda表达式来生成function, 通常情况下两种都是ok的,但是在参数多的时候,bind要传入很多的std::placeholders,而且看着没有lambda表达式直观,所以通常建议优先考虑使用lambda表达式。

http://blog.csdn.net/hongjunbj/article/details/8891387
           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/427524.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java轻松实现无锁队列

1、什么是无锁(Lock-Free)编程 当谈及 Lock-Free 编程时&#xff0c;我们常将其概念与 Mutex(互斥) 或 Lock(锁) 联系在一起&#xff0c;描述要在编程中尽量少使用这些锁结构&#xff0c;降低线程间互相阻塞的机会&#xff0c;以提高应用程序的性能。类同的概念还有 "Lock…

numpy数组按某一维度相加_Python数据分析之NumPy(高级篇)

​一些更高级的ndarray处理where和一些其他的逻辑运算np.where(cond,x,y)&#xff1a;满足条件(cond)输出x&#xff0c;不满足输出yx_arr np.array([1.1, 1.2, 1.3, 1.4, 1.5])y_arr np.array([2.1, 2.2, 2.3, 2.4, 2.5])cond np.array([True, False, True, True, False])pr…

Python入门:局部变量与全局变量2

例子1&#xff1a; names("Lili","Rain","Jack") change_name(name): names[0]"LiLy" print(names) 结果&#xff1a;names("LiLy","Rain","Jack") #列表可以在函数中直接修改 例子2&#xff1a; name…

md5与des算法有何不同_到底AI芯片和传统芯片有何区别?

前两天成立仅两年国内专做人工智能FPGA加速算法的初创公司深鉴科技被国际巨头赛灵思收购了&#xff0c;在业界引起不小的震动。目前国内做AI芯片的公司可谓不少了&#xff0c;AI芯片已然成为了当下芯片行业最热领域。但是大部分人对AI芯片的架构应该都不是太了解。那么AI 芯片和…

BlueTooth 蓝牙音频音质探讨

蓝牙音频音质探讨简介&#xff1a;本文简单介绍了蓝牙无线音频技术 A2DP&#xff0c;并从技术角度探讨其音质。1. 蓝牙 A2DP 简介我们先从蓝牙核心规范说起&#xff0c;目前支持最广泛的蓝牙 2.0/2.1 EDR 连接速率为 3Mbit/s&#xff0c;实际可用数据传输速率为 2.1Mbit/s。蓝…

Active Directory PowerShell模块收集AD信息

0x00 前言简介 Microsoft为Windows Server 2008 R2&#xff08;以及更高版本&#xff09;提供了多个Active Directory PowerShell cmdlet&#xff0c;这大大简化了以前需要将涉及到的ADSI冗长代码行放在一起的任务。 在Windows客户端上&#xff0c;需要安装远程服务器管理工具&…

anaconda对应python版本_Python基础——如何查看python版本、如何查看多个python版本

前言初学者来说&#xff0c;安装python过程是存在一定难度的。在安装过程中&#xff0c;可能安装了多个python版本&#xff0c;可能安装了anaconda导致有自带的python&#xff0c;同时本身电脑也安装了官方下载的python也茫然不知。导致可能有以下情况发生&#xff1a;1.pip in…

MATLAB统计与回归

11.1 前言統計的技巧與資料分析常常形影不離。一般統計使用加法、累加法、平均值&#xff0c;中間值等等&#xff0c;由於處理的對象是矩陣資料&#xff0c;故其基本統計之技巧已經廣為應用&#xff0c;其觀念也會在正常之運作中出現。統計學中比較特殊應用者為機率、亂數、常態…

如何快速理解读懂他人代码(下)——技巧学习篇

四、望文生义&#xff0c;进而推敲组件的作用 先建立系统的架构性认识&#xff0c;然后透过名称及命名惯例&#xff0c;就可以推测出各组件的作用。例如&#xff1a;当Winamp尝试着初始化一个Plug-In时&#xff0c;它会呼叫这个结构 中的init函式&#xff0c;以便让每个Plug-I…

yii2通过url访问类中的方法_每日学点---nginx变量使用方法详解(3)

也有一些内建变量是支持改写的&#xff0c;其中一个例子是 $args. 这个变量在读取时返回当前请求的 URL 参数串(即请求 URL 中问号后面的部分&#xff0c;如果有的话 )&#xff0c;而在赋值时可以直接修改参数串。我们来看一个例子&#xff1a;location /test { set $orig_args…

GOOGLE HACKING 系列文章 【FreeXploiT整理收集】

本文涉及作者 swap&#xff08;慕容小雨&#xff09;&#xff0c;zhaohuan&#xff08;Xfocus&#xff09;&#xff0c;snipe&#xff08;4ngel&#xff09;信息安全的隐患-GoogleHacking原理和防范作者&#xff1a;zhaohuanphack.org 来源&#xff1a;www.phack.org技术天地&a…

Openldap命令详解

Openldap 客户端常用管理命令 1、ldapadd -x: 简答认证方式-W: 不需要在命令上写密码 ldapapp -x -D "cnManager,dcsuixingpay,dccom" -W-w: password 需要命令上指定密码 ldapapp -x -D "cnManager,dcsuixingpay,dccom" -w 123456-H: 通过ldapapi-h: host…

用python画六瓣雪花_python-turtle-画雪花-2种方法及效果的详解

#python3.8#xuguojun#2020.1.30#导出模块&#xff0c;这样导出比代码较简洁&#xff0c;但是注意r和后面RGB的r&#xff0c;所以我改为d代替R&#xff08;r&#xff09; importturtle as timportrandom as r#绘制雪花 s30 #定义30个 defsnow(s): t.ht()#hthideturtle&#xff0…

2018年高考游记

2018年高考游记 在前言之前&#xff1a; 这篇文章已经写十几天吧 有心情时偶尔写上几段 也不知道自己抱着什么心态了&#xff0c;是留下一点回忆&#xff0c;还是给看得人启迪&#xff0c;还是...... 反正是要写出点东西来的 凡是现实的都是合乎理性的&#xff0c;凡是合乎理性…

小白学python需要多久_小白学Python | 你还在说你入不了门吗

收藏的好多啊 原创不易&#xff0c;动动小手&#xff0c;点个赞啦&#xff01;&#xff01; 十二月份&#xff0c;天气有时候会很阴沉&#xff0c;一天都见不到太阳。气温也慢慢变冷了&#xff0c;晚上回家还是会感觉到衣服穿少了。 阴阴沉沉总会过去的&#xff0c;我还是期待阳…

欧美剧集观看最佳索引 【2006-9-24更新】

allyesno&#xff1a;我在上两个月说要做一个美剧的网站 由于最近公司的事情一直很繁忙 我没有时间去做自己都积累了一大堆 美剧 日剧 恐怖片 没看 两个电脑的硬盘都塞的满满的 呵呵真是 天长地久有时尽,此恨绵绵无绝期。 哈哈~ 我现在正在构思 是不是把美剧网站列入公司的发展…

python语言format用法_详解Python中的format格式化函数的使用方法

format函数实现字符串格式化的功能 基本语法为&#xff1a; 通过 : 和 {} 来控制字符串的操作 一、对字符串进行操作 1. 不设置指定位置&#xff0c;按默认顺序插入 ①当参数个数等于{}个数的时候 str_1 "小明{}小美,可是小美{}小明".format("喜欢", &quo…

如何提高英文的科研写作能力

作为一个科研工作者&#xff0c;在国际学术期刊上发表科研论文是与同行交流、取得国际影响的必经之路。有些国内的科学家&#xff0c;实验做得很漂亮&#xff0c;但常常苦恼于论文的写作力不从心&#xff0c;成为国际交流的一大障碍。本文从博主的亲身体验出发&#xff0c;给博…

电子书专题

EXE电子书与垃圾 作者&#xff1a;马健邮箱&#xff1a;stronghorsetom.com主页&#xff1a;http://stronghorse.yeah.net 目前EXE格式的电子书在网络上比较流行&#xff0c;制作工具五花八门&#xff0c;eBook Workshop和eBook Edit Pro是其中比较流行的两个。但是又有多少人知…

python变量标识符_python中的变量和标识符

字面量&#xff1a; 就是一个一个的值&#xff0c;如1、2、3、‘hello’&#xff0c;就是它自己本身表达的字面值、字面意思&#xff0c;在程序中可以直接使用。 变量&#xff08;variable&#xff09;&#xff1a; 可以用来保存字面量&#xff0c;变量本身没有任何意思&#x…