C++11中新特性之:lambda 表达式

首先摆出Lambda表达式语法

lambda-expression:
  lambda-introducer lambda-declaratoropt compound-statement
lambda-introducer:
  [ lambda-captureopt ]

lambda-capture:
  capture-default
  capture-list
  capture-default , capture-list
capture-default:
  &
  =
capture-list:
  capture
  capture-list , capture
capture:
  identi?er
  & identi?er
  this
lambda-declarator:
  ( parameter-declaration-clause ) attribute-specifieropt mutableopt exception-specificationopt trailing-return-typeopt

然后用一张图表示他们的属性:

  1. lambda-introducer(也称为 capture 子句)
  2. lambda declarator(也称为参数列表)
  3. mutable(也称为可变规范)
  4. exception-specification(也称为异常规范)
  5. trailing-return-type(也称为返回类型)
  6. compound-statement(也称为 lambda 体)

之后是capture 子句(最让人搞不懂的地方)

一个 lambda 表达式实质上是一个类、构造函数和函数调用运算符。 就像你定义类时一样,在 lambda 中你必须要决定生成的对象是通过值还是引用捕获变量,或者根本不捕获。 如果一个 lambda 表达式必须访问局部变量和函数参数(但你可以访问static local variable),则必须捕获它们。 capture 子句(标准语法中的 lambda-introducer)指定 lambda 表达式的主体通过值还是引用访问封闭范围中的变量。 有与号 (&) 前缀的变量通过引用访问,没有该前缀的变量通过值访问。通过只访问局部变量是只读的。除非你加上了mutable。。

允许空 capture 子句 [ ] 指示 lambda 表达式的主体不访问封闭范围中的变量。

使用默认捕获模式(标准语法中的 capture-default)以通过值或引用捕获未指定的变量。 通过将 & 或 = 用作 capture 子句的第一个元素来指定默认捕获模式。 & 元素指示 lambda 表达式的主体通过引用访问未指定的变量。 = 元素指示 lambda 表达式的主体通过值访问未指定的变量。 例如,如果 lambda 体通过引用访问外部变量 total 并通过值访问外部变量 factor,则以下 capture 子句等效:

[&total, factor]
[factor, &total]
[&, factor]
[factor, &]
[=, &total]
[&total, =]

关于 capture-default 的一种常见的误解是,范围内的所有变量无论是否在 lambda 中使用,都会被捕获。 事实上并非如此 - 使用 capture-default 时,只有 lambda 中提及的变量才会被捕获。

如果 capture 子句包含 capture-default&,则该 capture 子句的 capture 中没有任何 identifier 可采用 & identifier 形式。 同样,如果 capture 子句包含 capture-default=,则该 capture 子句的 capture 不能采用 = identifier 形式。 identifier 或 this 在 capture 子句中出现的次数不能超过一次。 以下代码片段给出了一些示例。

struct S { void f(int i); };

void S::f(int i) {
    [&, i]{};    // OK
    [&, &i]{};   // ERROR: i preceded by & when & is the default
    [=, this]{}; // ERROR: this when = is the default
    [i, i]{};    // ERROR: i repeated
}

capture 后跟省略号是包扩展,如以下可变参数模板示例中所示:

template<class... Args>
void f(Args... args) {
    auto x = [args...] { return g(args...); };
    x();
}

在使用 capture 子句时,建议记住以下几点(尤其是使用采取多线程的 lambda 时):

  • 引用捕获可用于修改外部变量,而值捕获却不能实现此操作。 (mutable 允许修改副本,而不能修改原始项。美柚mutable连副本都不能修改)
  • 引用捕获会反映外部变量的更新,而值捕获却不会反映。
  • 引用捕获引入生存期依赖项,而值捕获却没有生存期依赖项。

参数列表

参数列表(标准语法中的 lambda declarator)是可选的,它类似于函数的参数列表。

由于参数列表是可选的,因此在不将参数传递到 lambda 表达式,并且其 lambda-declarator: 不包含 exception-specification、trailing-return-type 或 mutable 的情况下,可以省略空括号。(也就是后面直接接lambda体的)如

[&]  {int a, b = 1;a = b + 1; }();

可变规范

通常,lambda 的函数调用运算符为 const-by-value,但对 mutable 关键字的使用可将其取消。 它不会生成可变的数据成员。 利用可变规范,lambda 表达式的主体可以修改通过值捕获的变量。 本文后面的一些示例将显示如何使用 mutable。

异常规范

你可以使用 throw() 异常规范来指示 lambda 表达式不会引发任何异常。 与普通函数一样,如果 lambda 表达式声明 throw() 异常规范且 lambda 体引发异常将编译错误

返回类型

将自动推导 lambda 表达式的返回类型。 无需表示 auto 关键字,除非指定 trailing-return-type。 trailing-return-type 类似于普通方法或函数的返回类型部分。 但是,返回类型必须跟在参数列表的后面,你必须在返回类型前面包含 trailing-return-type 关键字 ->。

如果 lambda 体仅包含一个返回语句或其表达式不返回值,则可以省略 lambda 表达式的返回类型部分。 如果 lambda 体包含单个返回语句,编译器将从返回表达式的类型推导返回类型。 否则,编译器会将返回类型推导为 void。 下面的代码示例片段说明了这一原则。

auto x1 = [](int i){ return i; }; // OK: return type is int
auto x2 = []{ return{ 1, 2 }; };  // ERROR: return type is void, deducing
                                  // return type from braced-init-list is not valid
auto x2 = [](int i) -> initializer_list<int>{return {1, 2};};  //OK: return type is compatible

Lambda 体

lambda 表达式的 lambda 体(标准语法中的 compound-statement)可包含普通方法或函数的主体可包含的任何内容。 普通函数和 lambda 表达式的主体均可访问以下变量类型:

  • 参数
  • 本地声明变量
  • 类数据成员(在类内部声明并且捕获 this 时)
  • 具有静态存储持续时间的任何变量(例如,全局变量)

此外,lambda 表达式可以访问它从封闭范围中捕获的变量。 如果某个变量显示在 lambda 表达式的 capture 子句中,则该变量是显式捕获的。 否则,该变量是隐式捕获的。 lambda 表达式的主体使用默认捕获模式来访问隐式捕获的变量。

以下示例包含通过值显式捕获变量 n 并通过引用隐式捕获变量 m 的 lambda 表达式:

时间: 2024-08-07 00:22:11

C++11中新特性之:lambda 表达式的相关文章

C++11 新特性之 Lambda表达式

lambda表达式可以用于创建并定义匿名的函数对象,以简化编程工作 Lambda的语法如下: [函数对象参数](操作符重载函数参数)->返回值类型{函数体} []内的参数指的是Lambda表达式可以取得的变量.(2)函数中的param就是指函数可以得到在Lambda表达式外的全局变量, 如果在[]中传入=的话,即是可以取得所有的外部变量,如(1)和(3)Lambda表达式 ()内的参数是每次调用函数时传入的参数. ->后加上的是Lambda表达式返回值的类型,如(3)中返回了一个int类型的变

C++11新特性(3) lambda表达式(1)

C++11添加了一项名为lambda表达式的新功能.通过这项功能能编写内嵌的匿名函数,而不必编写独立函数或函数对象,使得代码更加理解. lambda表达式包含以下部分. [capture_block](parameters) mutable exception_specification->return_type {body} 现在分析各个部分的内容: (capture_block)捕捉块:指定如何捕捉所在作用域的变量,并供给lambda主体部分使用. (parameter)参数(可选):lam

JDK1.8新特性(二): Lambda表达式 (参数列表) -&gt; { } 和函数式接口@FunctionalInterface

Lambda表达式 二:简介 JDK的升级的目的有以下几个:增加新的功能.修复bug.性能优化.简化代码等几个方面,Lambda表达式就是属于简化代码,用于简化匿名实现类,提供一种更加简洁的写法.Lambda表达式在Swift语言中称之为代码块,Lambda表达式可以认为是一种特殊的接口,该接口必须只有一个抽象方法. 语法 (参数类型 参数名, 数参数类型 参数名2...) -> { // code }; 小括号()中的内容就是方法中的参数列表包括参数类型.参数名,其中参数类型是可以省略的,当参

jdk1.8新特性之lambda表达式及在Android Studio中的使用举例

Jdk1.8已经出很久了但是很多同学对它的特性在android studio 中的应用可能还不是很熟悉,今天我们就来对这个新特性在AS中做它的应用实践. 一.首先在有JDK1.8的情况下我们要在AS的设置中将这个地方设置成java1.8的. 二在build.gradle中添加上这两句 即: compileOptions {    sourceCompatibility 1.8    targetCompatibility 1.8} 这样就完成了使用之前的设置工作了.然后我们进入重要的应用. 那么

c++11 新特性之lambda表达式

写过c#之后,觉得c#里的lambda表达式和delegate配合使用,这样的机制用起来非常爽.c++11也有了lambda表达式,形式上有细小的差异.形式如下: c#:(input parameters) => {statement;} c++:[capture list](parameter list) -> return type {statement;} c++lambda表达式共分为4各部分.其中parameter list 和 return type 是可以在特定情况下省略的. 一

好程序员分享java8新特性之Lambda表达式

?.Lambda表达式简介 什么是Lambda? Lambda表达式是Java 8推出的?个新特性.从本质上讲,Lambda表达式是?个匿名函数. 为什么要使?Lambda? 使?Lambda表达式可以对?个接?进??常简洁的实现. 之前我们在给?个接?引?赋值的时候,可以使?接?实现类,或者匿名内部类.但是有了 Lambda表达式,我们可以更加?便的实现这个需求. interface Comparator<T>?{ int compare(T o1, T o2); } class Progr

2020了你还不会Java8新特性?Lambda表达式及API(二)

lambda表达式 为什么要使用lambda表示式 在Java中无法将函数座位参数传递给一个方法,也无法返回一个函数的方法. 在js中,函数的参数是一个函数.返回值是另一个函数的情况是非常常见的.是一门经典的函数式语言. Java匿名内部类. 匿名内部类的介绍 Gradle的使用.可以完全使用maven的中央仓库. 进行安卓的开发时,gradle已经成为标配了. lambda: 匿名内部类 my_jButton.addActionListener(new ActionListener() { @

jdk1.8新特性之lambda表达式

lambda表达式其实就是指一个匿名函数,应用最广泛的就是匿名内部类.在jdk1.8之前,我们定义一个匿名内部类可能需要写一大坨代码,现在有了lambda之后,可以写的很简洁了.但不是说lambda只能用来简化匿名内部类,从lambda的实际作用和表现上来看,就是一个变量指代一个代码块.而能够使用lambda表达式的一个前提要求是,该变量必须实现某个函数式接口.啥是函数式接口?参考jdk1.8新特性之函数式接口.看例子: 1.函数式接口 * Copyright (c) 1994, 2013, O

Java8新特性03 Lambda表达式

一. Lambda表达式概述 Lambda表达式是Java8中最大的变化.它允许我们将一个函数当作方法的参数,或者说把一段代码当作数据使用.很多基于JVM平台的语言一开始就支持Lambda表达式,比如Scala,但是Java语言一直只能使用匿名内部类来替代Lambda表达式,直到Java8才使用Lambda表达式. 二.定义 Lambda表达式是一个匿名函数,它是一个函数式接口的具体实现方式:使用Lambda语法可以用来代替Java8之前的匿名内部类,从而简化代码. 任意只包含一个抽象方法的接口