介绍

lambda表达式是一种局部类类型,它含有一个构造函数,和一个const成员函数operator()()。

lambda表达式除了能做参数外,还能用于初始化一个声明为auto或者std::function<R(LA)>的变量。R是lambda的返回类型,LA是它的类型参数列表。

组成部件

  1. 一个可能为空的捕获列表,指明定义环境中哪些名字能够被用在lambda表达式中,以及这些名字是被拷贝还是引用。捕获列表位于[]中。 补充: 静态变量不能也不需要被捕获。
  2. 一个可选的参数列表。指明所需要的参数,位于()中。补充 当无参数时,可以省略。
  3. 一个可选的mutable修饰符,当需要修改通过值捕获的变量的副本时,位于()之后。
  4. 一个可选的noexcept修饰符,表明无异常被抛出。位于mutable修饰符之后。
  5. 一个可选的->形式的返回类型声明,可与decltype一起搭配使用。
  6. 一个表达式体,指明要执行的代码,位于{}块中。

实现模型

对于lambda表达式而言,有着很多种表述方法,并且有着多中优化途径。如果把lambda表达式看成是一种定义并使用函数对象的便捷方式,那么便于理解。

假设有这么一个例子:

void print_modulo(const vector<int>& v, ostream& os, int m)
{for_each(begin(v), end(v),[&os, m](int x) {if (x % m == 0) os << x << '/n';});
}

我们可以将其中的lambda表达式改写成一个函数对象:

class Modulo_print
{ostream& os;int m;
public :Modulo_print(ostream& s, int mm) :os{ s }, m{ mm } {}void operator() (int x) const {if (x % m == 0) os << x << '/n';}
};

并且改写之前的例子:

void print_modulo(const vector<int>& v, ostream& os, int m)
{for_each(begin(v), end(v),Modulo_print{os, m});
}

我们把由lambda表达式生成的函数对象称为闭包对象 (闭包)

如果lambda表达式通过引用 ([&]) 捕获它定义环境中的每一个局部变量,则其闭包对象则可以优化为简单的包含一个指向外层栈框架的指针。

捕获

lambda表达式的主要用途是封装一部分代码以便将其用做参数。

有些lambda表达式无需访问它的局部环境,这样lambda表达式使用空引入符 [ ] 定义。

lambda引入符的形式有很多:

  1. [ ]空捕获列表,在lambda表达式内部无法使用其外层上下文中的任何局部名字,其数据需要从实参或者非局部变量中获得。
  2. [&]通过引用隐式捕获。所有局部变量通过引用使用。
  3. [=]通过值隐式捕获。所有局部变量通过拷贝使用,这些副本是在lambda表达式的调用点获得的。
  4. [捕获列表] 显式捕获。通过引用或者值方式捕获其局部变量。捕获列表中可以出现this (通过值方式捕获) 和 紧跟…的名字以表示的元素。
  5. [&, 捕获列表] 对于出现在捕获列表中的名字以值方式捕获,不能以&为名字前缀。捕获列表可以出现this。
  6. [=, 捕获列表] 对于出现在捕获列表中的名字以引用方式捕获,必须以&为前缀,捕获列表中不能出现this。

对于lambda表达式来说,当把lambda传递给另一个线程时,用值捕获更优,通过引用或者指针访问其他线程中的内容是一种危险操作,对于性能和正确性来说都是如此。

对于可变模板参数的捕获示例:

template <typename...Var>
void algo(int s, Var... var)
{
auto helper = [&s, &var...]{return s * (h1(var...) + h2(var...)); };
}

生命周期

lambda表达式的生命周期可能比它的被调用者更长。当我们把lambda表达式传递给另一个线程或者存在别处以供后续使用的时候。
例如:

void setup(Menu& m)
{
Point p1, p2, p3;
m.add("draw a triangle", [&]{m.draw(p1, p2, p3); }); //可能会发生程序错误
}

当setup完成之后,我们可能需要画一个三角型,此时lambda表达式将会访问一个已经不存在的局部变量。

如果我们发现一个lambda表达式的生命周期可能比它的调用者更长,则我们需要确保所有局部信息都已经被拷贝到闭包对象中去。

m.add("draw a triangle", [=]{m.draw(p1, p2, p3); });

lambda与this

当lambda表达式被用在成员函数当中,我们怎样去访问类成员变量呢?我们通过捕获this指针来访问类成员对象。
例如:

template <typename T>
class Request
{typedef typename vector<T>::value_type result;function<result(const vector<T>&)> oper;vector<T> values;result results;
public:void execute(){[this] {results = oper(values);};}
};

类成员变量是通过this访问的,在lambda表达式中[this] 和 [=] 互不兼容,因此一不小心就可能在多线程程序中造成竞争条件。

mutable的lambda表达式

如果希望在lambda表达式修改函数对象的状态,即修改通过值捕获的变量,则可以使用mutable修饰符:

void algo()
{int count = 100;[count]()mutable {return --count;};
}

lambda表达式与返回类型

lambda表达式的大多数规则都是从类和函数借鉴过来的,然而需要有两点需要注意:

  1. 如果lambda表达式不需要任何参数,则参数列表可以省略。因此lambda表达式的最简形式是[]{}。
  2. lambda表达式的返回类型可以有其本身推断得到,而函数(方法)不行。

如果在一条lambda表达式的主体部分不包含return语句,则其返回类型为void。
如果lambda表达式包含一条return语句,则返回类型是return表达式的类型。 其他情况,必须显式定义一个返回类型。

lambda表达式的类型

任意两个lambda表达式的类型都不相同,一旦两个lambda表达式的类型相同,则模板实例化机制就无法识别它们了。

例如:

auto algo = [&algo](char* b, char* a) {swap(*b, *--a); algo(++b, a);};

由于algo的类型并没有在使用之前被推断出来,因此上面的写法是错误的。

使用function包装器可以存入函数对象。就可以保证,在使用之前, algo的类型就已经知道了。

 function<void (char*b, char*a)>  algo = [&](char* b, char* a) {swap(*b, *--a); algo(++b, a);};

如果我们只是想给lambda表达式起个名字,而不会在主体内部递归的使用它。则:

auto algo = [&](char* b, char* a) {swap(*b, *--a); };

是没有问题的。

如果lambda表达式什么也不捕获,则我们可以把它赋值给一个指向正确类型的函数指针。

 double (*fun) (double a) = [](double a) {return sqrt(a);};

但行好事, 莫问前程!

C++——lambda表达式相关推荐

  1. Lambda 表达式基础理论与示例

    Lambda 表达式基础理论与示例 Lambda 表达式,也可称为闭包,推动 Java 8 发布的最重要新特性. Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中). 使用 Lam ...

  2. lambda表达式树

    一.定义: 表达式树又称为表达式目录树,以数据形式表示语言级代码.所有的数据都存储在树结构中,每个结点表示一个表达式(Expression). 二.要点: –Lambda表达式的参数类型可以忽略,因为 ...

  3. java 8 lambda reduce_JDK8新特性Lambda表达式体验

    "Lambda 表达式"(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstra ...

  4. java saf_Java 8 Lambda表达式探险

    为什么? 我们为什么需要Lambda表达式 主要有三个原因: > 更加紧凑的代码 比如Java中现有的匿名内部类以及监听器(listeners)和事件处理器(handlers)都显得很冗长 &g ...

  5. lamda表达式python_Python--13 lambda表达式

    python 允许使用lambda表达式来创建匿名函数 lambda表达式 >>>def ds(x): ... return 2 * x +1 ... >>> ds ...

  6. C++11:Lambda表达式(匿名函数)理解

    C++在C11标准中引入了匿名函数,即没有名字的临时函数,又称之为lambda表达式.lambda表达式 实质上是创建一个匿名函数/对象.即你可以理解为(Lambda 表达式实际上是一个函数,只是它没 ...

  7. java8 lambda 表达式详解

    lambada 表达式实质上是一个匿名方法,但该方法并非独立执行,而是用于实现由函数式接口定义的唯一抽象方法 使用 lambda 表达式时,会创建实现了函数式接口的一个匿名类实例 可以将 lambda ...

  8. Java8中Lambda表达式的10个例子

    Java8中Lambda表达式的10个例子  例1 用Lambda表达式实现Runnable接口 Java代码   //Before Java 8: new Thread(new Runnable() ...

  9. 【读书笔记】Lambda表达式

    Lambda表达式,是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型. Lambda表达式, 都是用"=>"运算符. 读作"goes t ...

  10. 【C++】lambda 表达式

    1.lambda 表达式 1.1 lambda 特点 lambda表示一个可调用单元,可视为内联函数 范式 : 具有一个返回类型,一个参数列表,一个函数体 [captrue list](paramet ...

最新文章

  1. oracle_dataGuard_11G
  2. MT8870DTMF解码器测试
  3. php数组如何写入txt文件中_php将数组写入到文件的三种方法
  4. 【PC工具】数学公式编辑器MathType v7.4.4.516你懂的版本
  5. 2016年春季计算机应用基础,东北师范2016年春季《计算机应用基础》期末考核
  6. container到image
  7. OpenCV视频进度播放控制
  8. Spark修炼之道(高级篇)——Spark源码阅读:第六节 Task提交
  9. [SDOI2008]Cave 洞穴勘测
  10. 送给初学.net兄弟们的一些话
  11. nginx配置文件简单理解(自用)
  12. 大小字节序的深入理解和鉴定系统字节序方法
  13. 从零开始学习python编程-从零开始学Python程序设计
  14. Excel-图表与文本框/图片的组合
  15. 用 js判断 一个数是否是素数(质数)_js 基础算法题(二)
  16. ASP.NET免费发送邮件|
  17. Opencv图像处理——水平线和垂直线的提取
  18. 关于skb_make_writable()函数
  19. 个性化智能推荐技术研究总结
  20. JAVA之利用集合操作学生数据

热门文章

  1. Anton and Fairy Tale CodeForces - 785C(二分+思维)
  2. libcrypto yum 安装_YUM 安装 VSFTP出错
  3. java arp 攻击_用JAVA代码实现ARP攻击 | 学步园
  4. python都有什么包_常用的Python包有哪些,如何快速掌握Python编程
  5. 算法分析设计--递归算法
  6. mapreduce的规约(Combiner)
  7. linux-shell命令之date【显示/设置系统日期/时间】
  8. LeetCode Hot100 ---- 最长相关专题(动态规划)
  9. [深度学习] 自然语言处理 --- 1.Attention
  10. 服务器端 python pdb 调试