`
美丽的小岛
  • 浏览: 297587 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

【转】C++ function、bind以及lamda表达式

    博客分类:
  • c
  • c++
 
阅读更多

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

A closure (also lexical closure, function closure or function value)is a function together with
a 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对象的例子:

    1. #include < functional>  
    2.    
    3. std::function< size_t (const char*) > print_func;  
    4.    
    5. /// normal function -> std::function object  
    6. size_t CPrint(const char*) { ... }  
    7. print_func = CPrint;  
    8. print_func("hello world"):  
    9.    
    10. /// functor -> std::function object  
    11. class CxxPrint  
    12. {  
    13. public:  
    14.     size_t operator()(const char*) { ... }  
    15. };  
    16. CxxPrint p;  
    17. print_func = p;  
    18. 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的用法:

    1. #include < functional>  
    2.    
    3. int Func(int x, int y);  
    4. auto bf1 = std::bind(Func, 10, std::placeholders::_1);  
    5. bf1(20); ///< same as Func(10, 20)  
    6.    
    7. class A  
    8. {  
    9. public:  
    10.     int Func(int x, int y);  
    11. };  
    12.    
    13. A a;  
    14. auto bf2 = std::bind(&A::Func, a, std::placeholders::_1, std::placeholders::_2);  
    15. bf2(10, 20); ///< same as a.Func(10, 20)  
    16.    
    17. std::function< int(int)> bf3 = std::bind(&A::Func, a, std::placeholders::_1, 100);  
    18. 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的庐山真面目:

    1. vector< int> vec;  
    2. /// 1. simple lambda  
    3. auto it = std::find_if(vec.begin(), vec.end(), [](int i) { return i > 50; });  
    4. class A  
    5. {  
    6. public:  
    7.     bool operator(int i) const { return i > 50; }  
    8. };  
    9. auto it = std::find_if(vec.begin(), vec.end(), A());  
    10.    
    11. /// 2. lambda return syntax  
    12. std::function< int(int)> square = [](int i) -> int { return i * i; }  
    13.    
    14. /// 3. lambda expr: capture of local variable  
    15. {  
    16.     int min_val = 10;  
    17.     int max_val = 1000;  
    18.    
    19.     auto it = std::find_if(vec.begin(), vec.end(), [=](int i) {  
    20.         return i > min_val && i < max_val;   
    21.         });  
    22.    
    23.     auto it = std::find_if(vec.begin(), vec.end(), [&](int i) {  
    24.         return i > min_val && i < max_val;  
    25.         });  
    26.    
    27.     auto it = std::find_if(vec.begin(), vec.end(), [=, &max_value](int i) {  
    28.         return i > min_val && i < max_val;  
    29.         });  
    30. }  
    31.    
    32. /// 4. lambda expr: capture of class member  
    33. class A  
    34. {  
    35. public:  
    36.     void DoSomething();  
    37.    
    38. private:  
    39.     std::vector<int>  m_vec;  
    40.     int               m_min_val;  
    41.     int               m_max_va;  
    42. };  
    43.    
    44. /// 4.1 capture member by this  
    45. void A::DoSomething()  
    46. {  
    47.     auto it = std::find_if(m_vec.begin(), m_vec.end(), [this](int i){  
    48.         return i > m_min_val && i < m_max_val; });  
    49. }  
    50.    
    51. /// 4.2 capture member by default pass-by-value  
    52. void A::DoSomething()  
    53. {  
    54.     auto it = std::find_if(m_vec.begin(), m_vec.end(), [=](int i){  
    55.         return i > m_min_val && i < m_max_val; });  
    56. }  
    57.    
    58. /// 4.3 capture member by default pass-by-reference  
    59. void A::DoSomething()  
    60. {  
    61.     auto it = std::find_if(m_vec.begin(), m_vec.end(), [&](int i){  
    62.         return i > m_min_val && i < m_max_val; });  
    63. }  

     

    上面的例子基本覆盖到了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表达式。

分享到:
评论

相关推荐

    C++ function、bind以及lamda表达式

    本文是C++0x系列的第四篇,主要是内容是C++0x中新增的lambda表达式, function对象和bind机制。之所以把这三块放在一起讲,是因为这三块之间有着非常密切的关系,通过对比学习,加深对这部分内容的理解。在开始之间...

    c++11 bind库使用实例

    c++11使用bind11的例子,对学习c++11 的新特性有所帮助。

    C++中function和bind是如何实现的

    C++里面的function是一个非常奇妙的东西,但是你想过他是如何实现的吗?作者在深刻理解了其中奥妙之后写就的精简版本。而且可以避免STL里面的function无法和Socket编程一起使用的问题——bind会无法正确解析。

    C++11中lambda、std::function和std:bind详解

    大家都知道C++11中增加了许多的新特性,下面在这篇文中我们就来聊一下lambda表达式,闭包,std::function以及std::bind。文中介绍的很详细,相信对大家具有一定的参考价值,有需要的朋友们下面来一起看看吧。

    C++中使用function和bind绑定类成员函数的方法详解

    &lt;hello&gt; pf = std::bind(&Test1::fun,t,2); pf(4); // return 0; } 输出的值是2,说明pf传进去的4并没有什么用,在bind的时候值已经绑定死了,但是pf()这样不给参数会报错。 如果我们想自定义参数,使用占位符,...

    通过c++11的std::bind及std::function实现类方法回调,模拟Qt实现信号槽

    c++11引入了std::bind及std::function,实现了函数的存储和绑定,即先将可调用的对象保存起来,在需要的时候再调用。定义了SignalObject信号类和SlotObject槽类,其中信号类中的 std::function(int)&gt; _call就是要...

    c++ bind函数源码分析

    c++ bind函数源码分析

    C++实现线程池

    /** 用法原理:通过派生类WorkItemBase的dowork方法来实现,线程处理任务 通过create任务创建线程,并且这些线程一直在for循环里等待事件监听 一旦任务栈里有数据了触发线程执行任务。 **/

    在Android JNI环境下使用C++ Lambda表达式等

    本apk包只是一个C++可用性的...1、使用std::function和std::bind实现sigslot机制; 2、使用C++11的Lambda表达式 3、在C++代码中回调Java对象的方法 详细描述请参考:http://blog.csdn.net/dyw/article/details/8099947

    C++ Lambda Story - From C++98 to C++20.pdf

    本书适⽤于所有喜欢了解现代 C++ 特性:Lambda 表达式 的 C++ 开发⼈员。 校阅历史 2019 年 03 ⽉ 25 ⽇ - 第⼀版上线! C++ Lambda Story - From C++98 to C++20.md 1/1/2023 3 / 92 2020 年 01 ⽉ 05 ⽇ - 语法、...

    C++ 11 std::function和std::bind使用详解

    主要介绍了C++ 11 std::function和std::bind使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    c++ boost bind

    boost stl一些常用函数的使用训练

    c/c++ 标准库 bind 函数详解

    bind函数定义在头文件 functional 中。...在这篇博客lambda 表达式 介绍 中,讨论了find_if的第三个参数的问题,当时是用lambda表达式解决的,有了bind函数后,也可以用bind函数解决。 解决办法:bin

    实现了类似std::function /bind /shared_ptr功能

    实现了标准库中的function和bind以及智能指针,linux/windows通用。由于公司GCC版本较老,嵌入式的gcc也无法由我来控制,windows上使用的一些类似智能指针,在linux上无法使用,甚是不爽,于是自己手动写了一份,...

    C++ STL 内 std::{bind/tuple/function} 简单实现

    首先是实现 function,这个比较简单,重载 operator() 就好,这里只实现对函数指针的包装 其次是实现 tuple,这个会比较绕,通过模板取第一个参数,然后用剩下的参数继续生成 tuple并继承,是一种递归的思想 有了 ...

    基于boost的bind与function的消息处理框架

    算是一个消息处理框架吧,用于说函数对象function与bind的基本用法; 比较适合入门boost的function与bind的基本用法

    bind bind bind bind

    bind bind bind bind

    关于Function中的bind()示例详解

    前言 bind()接受无数个参数,第一个参数是它生成的新函数的this指向,比如我传个window,不管它在何处调用,这个新... getX: function() { return this.x; } }; module.getX(); // 返回 81 var retrieveX = module.get

    C++ 回调函数

    回调函数在编程中经常使用到,这个代码简单明了的呈现了C++使用回调函数的方法。

Global site tag (gtag.js) - Google Analytics