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 表达式使用一对方括号作为开始的标识,类似于声明一个函数,只不过这个函数没有名字,也就是一个匿名函数。这个匿名函数接受两个参数,a和b;其返回值是一个 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 * ; };
auto关键字实际会将 lambda 表达式转换成一种类似于std::function的内部类型(但并不是std::function类型,虽然与std::function“兼容”)。所以,我们也可以这么写:
std::function<int()> lambda = [] () -> int { return val * ; };
如果你对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/
C++11 新特性:Lambda 表达式的更多相关文章
- 【转】C++11新特性——lambda表达式
C++11的一大亮点就是引入了Lambda表达式.利用Lambda表达式,可以方便的定义和创建匿名函数.对于C++这门语言来说来说,“Lambda表达式”或“匿名函数”这些概念听起来好像很深奥,但很多 ...
- C++11新特性 lambda表达式
C++11 添加了了一个名为lambda表达式的功能,可以用于添加匿名函数 语法: [capture_block](parameter) mutable exception_specification ...
- Java8新特性-Lambda表达式是什么?
目录 前言 匿名内部类 函数式接口 和 Lambda表达式语法 实现函数式接口并使用Lambda表达式: 所以Lambda表达式是什么? 实战应用 总结 前言 Java8新特性-Lambda表达式,好 ...
- 乐字节-Java8新特性-Lambda表达式
上一篇文章我们了解了Java8新特性-接口默认方法,接下来我们聊一聊Java8新特性之Lambda表达式. Lambda表达式(也称为闭包),它允许我们将函数当成参数传递给某个方法,或者把代码本身当作 ...
- Java 8 新特性 - Lambda表达式
Lambda表达式 vs 匿名类既然lambda表达式即将正式取代Java代码中的匿名内部类,那么有必要对二者做一个比较分析.一个关键的不同点就是关键字 this.匿名类的 this 关键字指向匿名类 ...
- C#10新特性-lambda 表达式和方法组的改进
C# 10 中对Lambda的语法和类型进行了多项改进: 1. Lambda自然类型 Lambda 表达式现在有时具有"自然"类型. 这意味着编译器通常可以推断出 lambda 表 ...
- 大数据之路week06--day03(jdk8新特性 Lambda表达式)
为什么使用Lambda表达式?(做为初学者接触这个新的语法,会很懵逼,说道理,我在接触到这一块的时候,语法规则我看到了也很懵逼,因为这个和逻辑的关系不是很大,但就是作为一种新的语法出现,一时间很难接受 ...
- jdk8的新特性 Lambda表达式
很多同学一开始接触Java8可能对Java8 Lambda表达式有点陌生. //这是一个普通的集合 List<Employee> list = em.selectEmployeeByLog ...
- JDK8的新特性——Lambda表达式
JDK8已经发布快4年的时间了,现在来谈它的新特性显得略微的有点“不合时宜”.尽管JDK8已不再“新”,但它的重要特性之一——Lambda表达式依然是不被大部分开发者所熟练运用,甚至不被开发者所熟知. ...
- java8新特性——Lambda表达式
上文中简单介绍了一下java8得一些新特性,与优点,也是为本次学习java8新特性制定一个学习的方向,后面几篇会根据上文中得新特性一一展开学习.本文就从java8新特性中比较重要的Lambda表达式开 ...
随机推荐
- regular expression 练习
练习有一个文件,文件名为output_1981.10.21.txt .下面使用Python: 读取文件名中的日期时间信息,并找出这一天是周几.将文件改名为output_YYYY-MM-DD-W.txt ...
- 转 虫师的selenium借助AutoIt识别上传(下载)详解
selenium借助AutoIt识别上传(下载)详解 2014-12-27 11:26 by 虫师, 755 阅读, 1 评论, 收藏, 编辑 AutoIt目前最新是v3版本,这是一个使用类似BAS ...
- 未能找到类型集或命名空间名称 "xxxxxx" (是否缺少using 指令或引用?)
“未能找到类型或命名空间名称XXXX”,以往遇到这种情况第一时间想到就是没有引用需要的dll. 但今天我反复检查了好几次,还是没有解决问题.我注意到除了错误信息,还有几个警告信息“未能解析引用的程序集 ...
- 打开u盘时提示是否要将其格式化的提示
早上打开电脑插入U盘后,发现U盘报以下错误:(心中一紧,昨晚写的文档还在其中呢) 修复方法: Win+R 输入cmd 打开 ,执行命令 chkdsk G: /f 其中G为损坏区域所在盘符,即U盘在电脑 ...
- Cocos2d-x动画工具类
1.此工具类的目的是为了方便运行动画.使用TexturePackerGUI工具能够导出plist文件和png图片,这里我演示样例图片叫bxjg.plist和bxjg.png ///////////// ...
- Linux内存管理Swap和Buffer Cache机制
Linux内存管理Swap和Buffer Cache机制 一个完整的Linux系统主要有存储管理,内存管理,文件系统和进程管理等几方面组成,贴出一些以前学习过的一个很好的文章.与大家共享!以下主要说明 ...
- PHP采集库-Snoopy.class.php
Snoopy是一个php类,用来模拟浏览器的功能,可以获取网页内容,发送表单. Snoopy的特点: 1.抓取网页的内容 fetch 2.抓取网页的文本内容 (去除HTML标签) fetchtext ...
- C 指针使用误区
/** *错误给指针赋常量 *知识点:指针存储内存地址 **/ #include <stdio.h>void main(){ //int *p_int = 123; //错误,不能直接给指 ...
- git的全局变量
git的全局变量可以用在命令行设置: git config --global user.name "litifeng" git config --global user.email ...
- iOS改变UINavigationBar导航条标题颜色和字体
转自:http://www.2cto.com/kf/201311/260409.html iOS 5 以后 UINavigationController 可以 改变UINavigationBar导航条 ...