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

参考文章:https://blogs.oracle.com/pcarlini/entry/c_1x_tidbits_lambda_expressions

或许,Lambda 表达式算得上是 C++ 11 新增特性中最激动人心的一个。这个全新的特性听起来很深奥,但却是很多其他语言早已提供(比如 C#)或者即将提供(比如 Java)的。简而言之,Lambda 表达式就是用于创建匿名函数的。GCC 4.5.x 和 Microsoft Visual Studio 早已提供了对 lambda 表达式的支持。在 GCC 4.7 中,默认是不开启 C++ 11 特性的,需要添加  -std=c++11 编译参数。而 VS2010 则默认开启。

 

为什么说 lambda 表达式如此激动人心呢?举一个例子。标准 C++ 库中有一个常用算法的库,其中提供了很多算法函数,比如 sort() 和 find()。这些函数通常需要提供一个“谓词函数 predicate function”。所谓谓词函数,就是进行一个操作用的临时函数。比如 find() 需要一个谓词,用于查找元素满足的条件;能够满足谓词函数的元素才会被查找出来。这样的谓词函数,使用临时的匿名函数,既可以减少函数数量,又会让代码变得清晰易读。

下面来看一个例子:

#include <algorithm>
#include <cmath>void abssort(float *x, unsigned N)
{std::sort(x,x + N,[](float a, float b) { return std::abs(a) < std::abs(b); });
}

从上面的例子来看,尽管支持 lambda 表达式,但 C++ 的语法看起来却很“神奇”。lambda 表达式使用一对方括号作为开始的标识,类似于声明一个函数,只不过这个函数没有名字,也就是一个匿名函数。这个匿名函数接受两个参数,ab;其返回值是一个 bool 类型的值,注意,返回值是自动推断的,不需要显式声明,不过这是有条件的!条件就是,lambda 表达式的语句只有一个 return。函数的作用是比较 a、b 的绝对值的大小。然后,在此例中,这个 lambda 表达式作为一个闭包被传递给 std::sort() 函数。

下面,我们来详细解释下这个神奇的语法到底代表着什么。

我们从另外一个例子开始: 

std::cout << [](float f) { return std::abs(f); } (-3.5);

输出值是什么?3.5!注意,这是一个函数对象(由 lambda 表达式生成),其实参是 -3.5,返回值是参数的绝对值。lambda 表达式的返回值类型是语言自动推断的,因为std::abs()的返回值就是 float。注意,前面我们也提到了,只有当 lambda 表达式中的语句“足够简单”,才能自动推断返回值类型。

C++ 11 的这种语法,其实就是匿名函数声明之后马上调用(否则的话,如果这个匿名函数既不调用,又不作为闭包传递给其它函数,那么这个匿名函数就没有什么用处)。如果你觉得奇怪,那么来看看 JavaScript 的这种写法:

function() {} ();function(a) {} (-3.5);

C++ 11 的写法完全类似 JavaScript 的语法。

如果我不想让 lambda 表达式自动推断类型,或者是 lambda 表达式的内容很复杂,不能自动推断怎么办?比如,std::abs(float)的返回值是 float,我想把它强制转型为 int。那么,此时,我们就必须显式指定 lambda 表达式返回值的类型:

std::cout << [](float f) -> int { return std::abs(f); } (-3.5);

这个语句与前面的不同之处在于,lambda 表达式的返回时不是 float 而是 int。也就是说,上面语句的输出值是 3。返回值类型的概念同普通的函数返回值类型是完全一样的。

当我们想引用一个 lambda 表达式时,我们可以使用auto关键字,例如:

auto lambda = [] () -> int { return val * 100; };

auto关键字实际会将 lambda 表达式转换成一种类似于std::function的内部类型(但并不是std::function类型,虽然与std::function“兼容”)。所以,我们也可以这么写:

std::function<int()> lambda = [] () -> int { return val * 100; };

如果你对std::function<int()>这种写法感到很神奇,可以查看 C++ 11 的有关std::function的用法。简单来说,std::function<int()>就是一个可调用对象模板类,代表一个可调用对象,接受 0 个参数,返回值是int。所以,当我们需要一个接受一个double作为参数,返回int的对象时,就可以写作:std::function<int(double)>

引入 lambda 表达式的前导符是一对方括号,称为 lambda 引入符(lambda-introducer)。lambda 引入符是有其自己的作用的,不仅仅是表明一个 lambda 表达式的开始那么简单。lambda 表达式可以使用与其相同范围 scope 内的变量。这个引入符的作用就是表明,其后的 lambda 表达式以何种方式使用(正式的术语是“捕获”)这些变量(这些变量能够在 lambda 表达式中被捕获,其实就是构成了一个闭包)。目前为止,我们看到的仅仅是一个空的方括号,其实,这个引入符是相当灵活的。例如:

float f0 = 1.0;
std::cout << [=](float f) { return f0 + std::abs(f); } (-3.5);

其输出值是 4.5。[=] 意味着,lambda 表达式以传值的形式捕获同范围内的变量。另外一个例子:

float f0 = 1.0;
std::cout << [&](float f) { return f0 += std::abs(f); } (-3.5);
std::cout << '\n' << f0 << '\n';

输出值是 4.5 和 4.5。[&] 表明,lambda 表达式以传引用的方式捕获外部变量。那么,下一个例子:

float f0 = 1.0;
std::cout << [=](float f) mutable { return f0 += std::abs(f); } (-3.5);
std::cout << '\n' << f0 << '\n';

这个例子很有趣。首先,[=]意味着,lambda 表达式以传值的形式捕获外部变量。C++ 11 标准说,如果以传值的形式捕获外部变量,那么,lambda 体不允许修改外部变量,对 f0 的任何修改都会引发编译错误。但是,注意,我们在 lambda 表达式前声明了mutable关键字,这就允许了 lambda 表达式体修改 f0 的值。因此,我们的例子本应报错,但是由于有 mutable 关键字,则不会报错。那么,你会觉得输出值是什么呢?答案是,4.5 和 1.0。为什么 f0 还是 1.0?因为我们是传值的,虽然在 lambda 表达式中对 f0 有了修改,但由于是传值的,外部的 f0 依然不会被修改。

上面的例子是,所有的变量要么传值,要么传引用。那么,是不是有混合机制呢?当然也有!比如下面的例子:

float f0 = 1.0f;
float f1 = 10.0f;
std::cout << [=, &f0](float a) { return f0 += f1 + std::abs(a); } (-3.5);
std::cout << '\n' << f0 << '\n'; 

这个例子的输出是 14.5 和 14.5。在这个例子中,f0 通过引用被捕获,而其它变量,比如 f1 则是通过值被捕获。

下面我们来总结下所有出现的 lambda 引入符:

  • []        // 不捕获任何外部变量
  • [=]      // 以值的形式捕获所有外部变量
  • [&]      // 以引用形式捕获所有外部变量
  • [x, &y] // x 以传值形式捕获,y 以引用形式捕获
  • [=, &z]// z 以引用形式捕获,其余变量以传值形式捕获
  • [&, x]  // x 以值的形式捕获,其余变量以引用形式捕获

另外有一点需要注意。对于[=][&]的形式,lambda 表达式可以直接使用 this 指针。但是,对于[]的形式,如果要使用 this 指针,必须显式传入:

[this]() { this->someFunc(); }(); 

至此,我们已经大致了解了 C++ 11 提供的 lambda 表达式的概念。建议通过结合 lambda 表达式与std::sort()std::for_each()这样的标准函数来尝试使用一下吧!

转自:https://www.devbean.net/2012/05/cpp11-lambda/

转载于:https://www.cnblogs.com/liushui-sky/p/5742007.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/372544.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

山东自考c语言程序设计停考了吗,2018山东自考停考专业有哪些

自考每年都会停考一批的专业以适应社会的发展&#xff0c;今年山东自考的停考专业有哪些&#xff1f;本文由学梯网小编整理发布&#xff0c;仅供参考。2018年山东自考停考专业有什么根据山东省教育考试院发布的《关于山东自学考试停考国际贸易(专科)等19个专业的通知》知悉&…

公开调用私有Java方法?

我们是Java开发人员&#xff0c;在Java中已知4种访问修饰符&#xff1a;私有&#xff0c;受保护&#xff0c;公共和包。 好吧&#xff0c;除了私有外&#xff0c;最后三个可以通过继承&#xff0c;相同的包或实例从类外部调用。 现在&#xff0c;常见的问题是&#xff0c;可以公…

Java 异常处理 try catch finally throws throw 的使用和解读(一)

最近的一个内部表决系统开发过程中&#xff0c;发现对异常处理还存在一些模棱两可的地方&#xff0c;所以想着整理一下主要涉及到&#xff1a;1.try catch finally throws throw 的使用和解读2.自定义异常的使用3.常见的运行异常 /** * Java 异常处理 * try catch finally thr…

C语言字符像素,返回字符串宽度 (以像素为单位)

[c]代码库#include #include #include #include int main(void){/* request auto detection */int gdriver DETECT, gmode, errorcode;int x 0, y 0;int i;char msg[80];/* initialize graphics and local variables */initgraph(&gdriver, &gmode, "");/…

Spring动态物业管理

静态和动态属性对于运营管理以及在生产级别更改系统行为都非常重要。 特别地&#xff0c;动态参数减少了服务中断。 本文展示了如何使用Quartz在Spring Applications中管理动态属性。 有关使用 Spring和Quartz集成提供“ 使用Spring和Quartz的多作业计划服务”的文章。 让我们看…

[BZOJ1005][HNOI2008]明明的烦恼

[BZOJ1005][HNOI2008]明明的烦恼 试题描述 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? 输入 第一行为N(0 < N < 1000),接下来N行,第i1行给出第i个节点的度数Di,如…

android调用so封装jni,GitHub - Michelle0716/AndroidJniDemo1: 安卓对c进行so文件打包,并以jni的形式进行调用...

AndroidJniDemo1安卓对c进行so文件打包&#xff0c;并以jni的形式进行调用项目中的部分app &#xff1a; 编译so文件jniDemo: 添加运行so文件开发环境&#xff1a;android studio 3.0.1(As3.0以上创建项目&#xff0c;选择支持c/c,项目会自动生成需要的配置&#xff0c;不需要在…

ADF任务流:页面片段的托管bean范围

介绍 当我们使用ADF任务流并需要实现一些特定于流的业务逻辑或存储一些与该流相关的信息时&#xff0c;我们通常使用pageFlowScope托管bean。 而且&#xff0c;当我们需要为流的活动&#xff08;页面或页面片段&#xff09;提供服务时&#xff0c;我们将较短的作用域用于此类托…

Linux平台下:块设备、裸设备、ASMlib、Udev相关关系

对磁盘设备&#xff08;裸分区&#xff09;的访问方式分为两种&#xff1a;1.字符方式访问&#xff08;裸设备&#xff09;&#xff1b;2.块方式访问 Solaris平台 : 在Solaris平台下&#xff0c;系统同时提供对磁盘设备的字符、块方式访问。每个磁盘有两个设备文件名: 一个在/d…

Error0---local variable S is accessed from within inner class; needs to be declared final

local variable S is accessed from within inner class; needs to be declared final在内部类当中不能引用本地变量s,需要被声明为常量转载于:https://www.cnblogs.com/Cherrylalala/p/6636642.html

android版本如何修改时间,如何修改Android系统默认时间

相信很多人看到过Android手机或平板显示XXXX-01-01这个奇怪的日期&#xff0c;没错&#xff0c;这就是Android设备的默认日期。当Android设备没有联网&#xff0c;无法获取当前真实时间的时候&#xff0c;就会使用系统默认时间。大部分时候&#xff0c;系统默认时间是Epoch时间…

Java Web应用程序的反跨站点脚本(XSS)过滤器

这是为Java Web应用程序编写的一个好简单的反跨站点脚本&#xff08;XSS&#xff09;过滤器。 它的主要作用是从请求参数中删除所有可疑字符串&#xff0c;然后将其返回给应用程序。 这是我以前关于该主题的帖子的改进。 您应该将其配置为链&#xff08;web.xml&#xff09;中的…

生成建表脚本up_CreateTable

已经很久没用使用这个脚本了&#xff0c;今天用到&#xff0c;并做修改&#xff0c;增加了生成扩展属性功能。 Go if object_ID([up_CreateTable]) is not nullDrop Procedure [up_CreateTable] Go /* 生成建表脚本(V4.0) Andy 2017-3-28 */ Create Proc up_CreateTable (obje…

android程序员周记,程序员实习周记100篇

程序员实习周记100篇有效防止雷同&#xff01;简单修改即可使用&#xff01;姓名&#xff1a;XXX学号&#xff1a;20190920008专业&#xff1a;M]指导老师&#xff1a;实习时间&#xff1a;20XX-XX-XX—20XX-XX-XX2019年XX月XX日t8in6Ay8Cw7c HuktN6ttTE12V7A eZu9g e7W1Y Dxqx…

Python之装饰器

装饰器功能有两点&#xff1a;1.首先自动执行装饰器后面跟的这个函数&#xff0c;并将装饰器修饰的那个函数名作为参数带入装饰器后面函数&#xff1b;2.将装饰器后面函数的返回值&#xff0c;赋值给装饰器所修饰的那个函数。举个例子说明&#xff1a; 1 def outer(func):2 …

在独立Java应用程序中使用Tomcat JDBC连接池

这是从我们的客人文章W4G伙伴克拉伦斯豪的作者临春3从A按。 您可能会在文章结尾找到本书的折扣券代码&#xff0c;仅适用于Java Code Geeks的读者&#xff01; 请享用&#xff01; 在需要数据访问权限的独立Java应用程序中使用JDBC连接池时&#xff0c;大多数开发人员将使用com…

Python之路【目录】 2

http://www.cnblogs.com/wupeiqi/articles/4938499.html转载于:https://www.cnblogs.com/cp-miao/p/5750211.html

vs2019 缺android sdk,VS2019由于缺少NuGet Microsoft.NET.Sdk.Functions程序包而无法加载项目,但也无法添加此程序包(示例代码)...

我在解决方案中拥有的一个项目未在VSE2019中加载。它将引发此错误&#xff1a;C:MyProgramsrcMyProgram.Functions.csproj : error : The project file cannot be opened by the project system, because it is missing some critical imports or the referenced SDK cannot be…

数据库事物操作

事务 什么是事务?转账&#xff1a;1. 给wc账户减1000元2. 给wcxf账户加1000元 当给wc账户减1000元后&#xff0c;抛出了异常&#xff01;这会怎么样呢&#xff1f;我相信从此之后&#xff0c;wc再也不敢转账了。 使用事务就可以处理这一问题&#xff1a;把多个对数据库的操作绑…

通过OpenShift超越云技术

您是否厌倦了为您的应用程序请求新的开发机器&#xff1f; 您是否为应用程序设置新的测试环境感到烦恼&#xff1f; 您是否只想专注于和平开发应用程序而不会一直“沉迷于堆栈”&#xff1f; 我们听到你的声音。 我们也去过那里。 不用担心&#xff0c;OpenShift就在这里&#…