【转载】C++ function、bind和lambda表达式

本篇随笔为转载,原贴地址:C++ function、bind和lambda表达式

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      #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的庐山真面目:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      vector< int> vec;
      /// 1. simple lambda
      auto 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 syntax
      std::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 member
      class A
      {
      public:
          void DoSomething();
       
      private:
          std::vector<int>  m_vec;
          int               m_min_val;
          int               m_max_va;
      };
       
      /// 4.1 capture member by this
      void 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-value
      void 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-reference
      void 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表达式。

时间: 2024-10-08 10:19:06

【转载】C++ function、bind和lambda表达式的相关文章

【转帖】漫话C++0x(四) —- function, bind和lambda

实在是觉得此文总是去翻感觉不太好.于是果断转过来了,想看原文的请戳:http://www.wuzesheng.com/?p=2032 本文是C++0x系列的第四篇,主要是内容是C++0x中新增的lambda表达式, function对象和bind机制.之所以把这三块放在一起讲,是因为这三块之间有着非常密切的关系,通过对比学习,加深对这部分内容的理解.在开始之间,首先要讲一个概念,closure(闭包),这个概念是理解lambda的基础.下面我们来看看wikipedia上对于计算机领域的closu

C++ 11 Lambda表达式、auto、function、bind、final、override

接触了cocos2dx 3.0,就必须得看C++ 11了.有分享过帖子:[转帖]漫话C++0x(四) —- function, bind和lambda.其实最后的Lambda没太怎么看懂. 看不懂没关系,会用就行.可惜是连用都要思考半天.其实,查找根源是定义没有搞明白. 以后买东西,用之前,先看说明书才是必要的. ---------------------------------开始正文粘贴----------------------------------------- 一.Lambda表达式

C++11中的Lambda表达式

本文地址:http://www.cnblogs.com/archimedes/p/c11-lambda.html,转载请注明源地址. “Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数.Lambda表达式可以表示闭包(注意和数学传统意义上的不同). ISO C++ 11 标准的一大亮点是引入Lambda表达式.基本语法如

Lambda 表达式的演示样例-来源(MSDN)

本文演示怎样在你的程序中使用 lambda 表达式. 有关 lambda 表达式的概述.请參阅 C++ 中的 Lambda 表达式. 有关 lambda 表达式结构的具体信息,请參阅 Lambda 表达式语法. 本文内容 声明 Lambda 表达式 调用 Lambda 表达式 嵌套 Lambda 表达式 高阶 Lambda 函数 在函数中使用 Lambda 表达式 配合使用 Lambda 表达式和模板 处理异常 配合使用 Lambda 表达式和托管类型 声明 Lambda 表达式 演示样例 1

Lambda 表达式的示例-来源(MSDN)

本文演示如何在你的程序中使用 lambda 表达式. 有关 lambda 表达式的概述,请参阅 C++ 中的 Lambda 表达式. 有关 lambda 表达式结构的详细信息,请参阅 Lambda 表达式语法. 本文内容 声明 Lambda 表达式 调用 Lambda 表达式 嵌套 Lambda 表达式 高阶 Lambda 函数 在函数中使用 Lambda 表达式 配合使用 Lambda 表达式和模板 处理异常 配合使用 Lambda 表达式和托管类型 声明 Lambda 表达式 示例 1 由于

Lambda 表达式的示例

本文中的过程演示如何使用 lambda 表达式. Lambda Expressions in C++.' data-guid="411656d77b666e51e15caac8de528191">有关 lambda 表达式的概述,请参见 C++ 中的 Lambda 表达式. Lambda Expression Syntax.' data-guid="df40a218a0617e5e01e20ad28527a334">有关 lambda 表达式结构的更多信

复合Lambda表达式

import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; /** * 复合Lambda表达式 */ public class Demo { public static <T> List<T>

c++ 11学习笔记--Lambda 表达式(对比测试Lambda ,bind,Function Object)

所有c++ coder都应该为这个语法感到高兴,说的直白一点,Lambda 表达式就是函数对象的语法糖. 还是直接看对比栗子吧,抄袭的是msdn的官网 该示例使用 for_each 函数调用中嵌入的 lambda 向控制台打印 vector 对象中的每个元素是偶数还是奇数. 使用lambda #include <algorithm> #include <iostream> #include <vector> using namespace std; int main()

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

http://blog.csdn.net/augusdi/article/details/11771699 首先说明一点,回调函数按我的理解就是函数名称(即函数的地址)作为函数参数在另一个函数中被使用. function #include < functional> std::function< size_t (const char*) > print_func; //std::function< size_t (const char*) >是类型,print_func