C++ 中的 Lambda 表达式

  1、一个简单的 Lambda 表达式如下:
  
  [] {};
  
  这就定义了一个对象,这个对象匿名,记住,Lambda 表达式是对象,不是类型,这很重要!本例中,该对象的类型是 ‘anonymous-namespace‘::<lambda0>,这是编译器给它设的一个类型名。
  
  2、一般地,编译器实现 Lambda 表达式时,将其转化为函数对象(仿函数 functor)。比如上面的例子将转化为
  
  struct A {
  
  public:
  
  void operator()() const {}
  
  };
  
  A();
  
  其中 A() 是一个函数对象,而 A 是类型,这些要明确区分。
  
  3、仿函数和 Lambda 表达式是可以有状态的,也就是说它们可以含数据成员,而函数指针就无状态可言,比如下面这种情况就无法用函数指针,只能用仿函数或 Lambda 表达式。
  
  /********************************************************************
  
  created: 2014/05/07 22:31
  
  filename: main.cpp
  
  author: Justme0 (http://blog.csdn.net/justme0)
  
  purpose: 涉及状态时,只能用仿函数或 Lambda 表达式,无法用函数指针
  
  *********************************************************************/
  
  #include <iostream>
  
  #include <vector>
  
  #include <algorithm>
  
  using namespace std;
  
  /*
  
  ** 输出 vec 中大于 standard 的元素
  
  */
  
  void print_use_lambda(const vector<int> &vec, int standard) {
  
  for_each(vec.begin(), vec.end(), [standard](int elem) {
  
  if (standard < elem) {
  
  cout << elem << ‘ ‘;
  
  }
  
  });
  
  }
  
  struct Printor {
  
  private:
  
  int m_standard;
  
  public:
  
  Printor(int standard) : m_standard(standard) {}
  
  void operator()(int elem) const {
  
  if (m_standard < elem) {
  
  cout << elem << ‘ ‘;
  
  }
  
  }
  
  };
  
  /*
  
  ** 输出 vec 中大于 standard 的元素
  
  */
  
  void print_use_functor(const vector<int> &vec, int standard) {
  
  for_each(vec.begin(), vec.end(), Printor(standard));
  
  }
  
  int main(int argc, char **argv) {
  
  int arr[] = {5, 2, 1, 4, 5, 6, 7, 2};
  
  int size = sizeof arr / sizeof *arr;
  
  vector<int> vec(arr, arr + size);
  
  cout << "需要输出大于几的数?" << endl;
  
  int standard;
  
  cin >> standard;
  
  print_use_functor(vec, standard);
  
  cout << endl;
  
  print_use_lambda(vec, standard);
  
  cout << endl;
  
  system("PAUSE");
  
  return 0;
  
  }
  
  程序中调用 for_each 时,只输出大于某个数的元素,而这个数事先不知道,这个数代表了仿函数的状态,而若用函数指针则无法获得这个数(状态),注意 for_each 的第三个参数是一元的。
  
  4、按值方式传递捕捉列表与按引用方式传递列表不同,以下面的程序为例:
  
  /********************************************************************
  
  created: 2014/05/06 21:01
  
  filename: main2.cpp
  
  author: Justme0 (http://blog.csdn.net/justme0)
  
  purpose: Lambda 的捕捉列表
  
  *********************************************************************/
  
  #include <iostream>
  
  using namespace std;
  
  int main(int argc, char **argv) {
  
  int i = 0;
  
  auto by_val_lambda = [i] {
  
  return i;
  
  };
  
  auto by_ref_lambda = [&i] {
  
  return i;
  
  };
  
  cout << by_val_lambda() << endl; // 0
  
  cout << by_ref_lambda() << endl; // 0
  
  ++i;
  
  cout << by_val_lambda() << endl; // 0
  
  cout << by_ref_lambda() << endl; // 1
  
  system("PAUSE");
  
  return 0;
  
  }
  
  要解释这个问题,把它们转化为仿函数(编译器的实现)就一目了然了:
  
  /********************************************************************
  
  created: 2014/05/06 20:39
  
  filename: main.cpp
  
  author: Justme0 (http://blog.csdn.net/justme0)
  
  purpose: 用 Functor 代替 Lambda
  
  *********************************************************************/
  
  #include <iostream>
  
  using namespace std;
  
  struct Value {
  
  private:
  
  int m_i;
  
  public:
  
  Value(int i) : m_i(i) {}
  
  int operator()() const {
  
  return m_i;
  
  }
  
  };
  
  struct Reference {
  
  private:
  
  int &m_i;
  
  public:
  
  Reference(int &i) : m_i(i) {} // 必须用初始化列表
  
  int operator()() const {
  
  return m_i;
  
  }
  
  };
  
  struct Pointer {
  
  private:
  
  int *m_pi;
  
  public:
  
  Pointer(int *pi) : m_pi(pi) {}
  
  int operator()() const {
  
  return *m_pi;
  
  }
  
  };
  
  int main(int argc, char **argv) {
  
  int i = 0;
  
  auto by_val_functor = Value(i);
  
  auto by_ref_functor = Reference(i);
  
  auto by_ptr_functor = Pointer(&i);
  
  cout << by_val_functor() << endl; // 0
  
  cout << by_ref_functor() << endl; // 0
  
  cout << by_ptr_functor() << endl; // 0
  
  ++i;
  
  cout << by_val_functor() << endl; // 0
  
  cout << by_ref_functor() << endl; // 1
  
  cout << by_ptr_functor() << endl; // 1
  
  system("PAUSE");
  
  return 0;
  
  }
  
  值传递时 Lambda 保存了一个副本,与形参就无关了。引用传递时,Lambda 保存的是形参的引用,所以后面改变形参 ++i 时,Lambda 内保存的引用指向的 i 变了(同一个 i),注意指针与引用类似,用指针来解释引用再好不过了。
  
  5、Lambda 捕捉列表仅能捕捉父作用域的自动变量。
  
  标准是这么定义的,但有的编译器不遵守,笔者对具体的规则也存有疑惑。但总的来说,Lambda 的思想是局限于一个局部作用域中使用,若需全局共享,则用函数指针(无状态)或仿函数(有状态)。

猜你喜欢

转载自www.cnblogs.com/aquariusunny/p/12729793.html
今日推荐