浅析C++11新特性的Lambda表达式

lambda简介

熟悉Python的程序员应该对lambda不陌生。简单来说,lambda就是一个匿名的可调用代码块。在C++11新标准中,lambda具有如下格式:

[capture list] (parameter list) -> return type { function body }

可以看到,他有四个组成部分:

1.capture list: 捕获列表

2.parameter list: 参数列表

3.return type: 返回类型

4.function body: 执行代码

其中,参数列表和返回类型可以忽略。

下面,具体看几个简单的例子:

auto f1 = [] { return 1; };
auto f2 = [] () { return 2; };
cout<<f1()<<'\t'<<f2()<<endl;

捕获列表

lambda中的捕获列表既可以捕获值,也可以捕获引用。

捕获值:

int test_data[] = {1, 5, 9, 7, 3, 19, 13, 17};
int border = 8;
auto f3 = [border](const int &i){ if(i > border) cout<<i<<'\t'; };
for_each(begin(test_data), end(test_data), f3);
cout<<endl;

捕获引用:

auto f4 = [&border](const int &i){ if(i > border) cout<<i<<'\t'; };
border = 6;
for_each(begin(test_data), end(test_data), f4);
cout<<endl;

通过输出可以看出,lambda中起作用的border是修改后的6,证实了捕获的确是是引用。

需要注意的是,在捕获引用时,需要保证当lambda被调用时,此引用仍然有效。

捕获列表还可以采用隐式捕获的方式,即让编译器通过lambda的执行代码来判断需要捕获哪些局部变量。

隐式捕获可以捕获值、引用或者两者混合:

char space = ' ';
auto f5 = [=](const int &i){ if(i > border) cout<<i<<'\t'; };
auto f6 = [&](const int &i){ if(i > border) cout<<i<<'\t'; };
auto f7 = [&, space](const int &i){ if(i > border) cout<<i<<space; };
border = 0;
for_each(begin(test_data), end(test_data), f5);
cout<<endl;
for_each(begin(test_data), end(test_data), f6);
cout<<endl;
for_each(begin(test_data), end(test_data), f7);
cout<<endl;

这里的f7使用的混合形式,可以读作“除了space捕获值之外,其他变量均捕获引用”。

可变lambda

lambda需要在其中修改被值捕获的变量的值时,需要给lambda加上mutable关键字。否则会有编译错误。

auto f8 = [&, space](const int &i) mutable { if(i > border) {cout<<i<<space; space='\t';} };
for_each(begin(test_data), end(test_data), f8);
cout<<endl;
cout<<1<<space<<2<<endl;

从输出中可以看出,spacelambda f8中的值,在第一次调用之后,就被变成了制表符Tab;但是在lambda之外,space仍然是空格。

返回类型

lambda的返回类型采用尾置返回类型的方式。一般的:

1.lambda如果只包含return语句,则编译器可以推断其返回类型,此时可以不显示指定返回类型;

2.否则,编译器假定lambda返回void,而返回void的函数不可以反悔任何具体值,这在大多数情况下是个矛盾,因此需要显示指定返回类型。

但是,经过实际测试,目前的g++编译器更聪明了:对于第2点,目前只要编译器可以从lambda函数体中推断出函数的返回类型,就不需要显式指定返回类型,例如:

auto f9 = [](const int i){if(i % 3) return i * 3; else return i;};
transform(begin(test_data), end(test_data), begin(test_data), f9);
border = 0;
for_each(begin(test_data), end(test_data), f6);
cout<<endl;

lambda代码块中有多个return语句,并且还有if/else语句,但是编译器可以根据return语句推断出,其返回值应该是一个int类型,所以可以省略尾置返回类型。

但是,像下面这种形式,由于编译器在推断返回类型时发现了不一致,所以必须显式的指定返回类型:

auto f10 = [](const int i) -> double
{if(i % 5) return i * 5.0; else return i;};
transform(begin(test_data), end(test_data), begin(test_data), f10);
for_each(begin(test_data), end(test_data), f6);
cout<<endl;

总结

1.lambda表达式形式: [capture list] (parameter list) -> return type { function body },其中parameter list和return type可以省略。

2.捕获列表可以捕获值[val],也可以捕获引用[&ref]。

3.捕获列表还可以隐式捕获局部变量,同样有捕获值[=]和捕获引用[&]两种方式,初次之外还可以混合捕获[&, val]或者[=, &ref]

4.当lambda需要修改捕获的值时,需要加上mutable关键字。

4.当lambda无法自动推断出返回值类型时,需要通过尾置返回类型的方式显示指定。

以上就是C++11新特性之Lambda表达式的全部内容,希望本文对大家学习C++有所帮助。

(0)

相关推荐

  • 结合C++11新特性来学习C++中lambda表达式的用法

    在 C++ 11 中,lambda 表达式(通常称为 "lambda")是一种在被调用的位置或作为参数传递给函数的位置定义匿名函数对象的简便方法. Lambda 通常用于封装传递给算法或异步方法的少量代码行. 本文定义了 lambda 是什么,将 lambda 与其他编程技术进行比较,描述其优点,并提供一个基本示例. Lambda 表达式的各部分 ISO C++ 标准展示了作为第三个参数传递给 std::sort() 函数的简单 lambda: #include <algorit

  • C++ 中lambda表达式的编译器实现原理

    什么是Lambda? C++ 11加入了一个非常重要的特性--Lambda表达式.营里(戴维营)的兄弟都对Objective-C很熟悉,许多人多block情有独钟,将各种回调函数.代理通通都用它来实现.甚至有人选择用FBKVOController.BlocksKit等开源框架将KVO.控件事件处理都改为通过block解决.原因就是简单.方便.直观,函数的定义和使用出现在同一个地方.这里的Lambda表达式实际上和block非常类似,当然如果你用它和Swift语言的闭包比较,那就是一回事了. 现在

  • 实例讲解C++编程中lambda表达式的使用

    函数对象与Lambdas 你编写代码时,尤其是使用 STL 算法时,可能会使用函数指针和函数对象来解决问题和执行计算.函数指针和函数对象各有利弊.例如,函数指针具有最低的语法开销,但不保持范围内的状态,函数对象可保持状态,但需要类定义的语法开销. lambda 结合了函数指针和函数对象的优点并避免其缺点.lambda 与函数对象相似的是灵活并且可以保持状态,但不同的是其简洁的语法不需要显式类定义. 使用lambda,相比等效的函数对象代码,您可以写出不太复杂并且不容易出错的代码. 下面的示例比较

  • C++ 中的Lambda表达式写法

    小喵的唠叨话: 寒假之后,小喵在家里无所事事,最近用C++写代码的时候,用到了std::sort这个函数,每次用这个函数,小喵似乎都得查一下lambda表达式的写法.正好最近很闲,不如总结一下. 在Bing上搜索 C++ lambda ,第一条记录就是MSDN上的C++ lambda的介绍.本文也是基于这篇文章来写的. 那么接下来,我们分几个部分来介绍. 一.什么是Lambda表达式 MSDN上对lambda表达式的解释: 在 C++ 11 中,lambda 表达式(通常称为 "lambda&q

  • 基于C++ Lambda表达式的程序优化

    什么是Lambda? C++ 11加入了一个非常重要的特性--Lambda表达式.营里(戴维营)的兄弟都对Objective-C很熟悉,许多人多block情有独钟,将各种回调函数.代理通通都用它来实现.甚至有人选择用FBKVOController.BlocksKit等开源框架将KVO.控件事件处理都改为通过block解决.原因就是简单.方便.直观,函数的定义和使用出现在同一个地方.这里的Lambda表达式实际上和block非常类似,当然如果你用它和Swift语言的闭包比较,那就是一回事了. 这是

  • 浅谈C++11新引入的lambda表达式

    ISO C++ 11 标准的一大亮点是引入Lambda表达式.基本语法如下: [capture list] (parameter list) ->return type { function body } 简单的讲一下各个部分的作用 1.[capture list]捕获列表,捕获到函数体中,使得函数体可以访问 2.(parameter list)参数列表,用来表示lambda表达式的参数列表 3.->return type函数返回值 {function body}就是函数体 lambda表达式

  • C++中的Lambda表达式详解

    我是搞C++的 一直都在提醒自己,我是搞C++的:但是当C++11出来这么长时间了,我却没有跟着队伍走,发现很对不起自己的身份,也还好,发现自己也有段时间没有写C++代码了.今天看到了C++中的Lambda表达式,虽然用过C#的,但是C++的,一直没有用,也不知道怎么用,就可怜的连Lambda语法都看不懂.好了,这里就对C++中的Lambda进行一个简单的总结,就算是对自己的一个交代,我是搞C++的,我是一个C++ programmer. 一段简单的Code 我也不是文艺的人,对于Lambda的

  • 浅析C++11新特性的Lambda表达式

    lambda简介 熟悉Python的程序员应该对lambda不陌生.简单来说,lambda就是一个匿名的可调用代码块.在C++11新标准中,lambda具有如下格式: [capture list] (parameter list) -> return type { function body } 可以看到,他有四个组成部分: 1.capture list: 捕获列表 2.parameter list: 参数列表 3.return type: 返回类型 4.function body: 执行代码

  • 深入浅析JDK8新特性之Lambda表达式

    第一次是接触Lambda表达式是在TypeScript中(JavaScript的超集中),当时是为了让TypeScript的this方法外而不是本方法内所使用的.使用过后突然想到Lambda不是JDK8的重量级新特性么?于是感觉查阅相关资料并记录下来: 一. 行为参数化 行为参数化简单的说就是函数的主体仅包含模板类通用代码,而一些会随着业务场景而变化的逻辑则以参数的形式传递到函数之中,采用行为参数化可以让程序更加的通用,以应对频繁变更的需求. 考虑一个业务场景,假设我们需要通过程序对苹果进行筛选

  • Java8新特性:Lambda表达式之方法引用详解

    1.方法引用简述 方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法.方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文.计算时,方法引用会创建函数式接口的一个实例. 当Lambda表达式中只是执行一个方法调用时,不用Lambda表达式,直接通过方法引用的形式可读性更高一些.方法引用是一种更简洁易懂的Lambda表达式. Lambda表达式全文详情地址:http://blog.csdn.net/sun_promise/article/details/

  • Java8新特性之Lambda表达式的使用

    1. lambda表达式介绍 lambda表达式是Java8提供的新特性之一,也可以称之为闭包:它支持Java能够进行简单的函数式编程,也就是说可以把一个匿名函数作为一个方法的参数进行传递:其格式分为三部分,第一部分为入参列表,第二部由->固定组成,第三部分为方法体: public class LambdaTest { public static void main(String[] args) { // 使用lambda表达式创建线程 Thread thread = new Thread(()

  • 简单易懂的java8新特性之lambda表达式知识总结

    一.概念 从本质上来说,它就是一个匿名函数,可以用来直接实现接口中的方法,从而简化代码.但是Lambda有一个限制,不能实现接口中的所有方法,所以Lambda表达式只能用于有且仅有一个必须需要实现的方法接口,这里需要注意必须需要实现这六个字. public interface Printer { //有一个需要实现的方法,可以使用Lambda表达式 void print(); } public interface Printer { //有一个需要实现的方法,可以使用Lambda表达式 void

  • 深入理解Java8新特性之Lambda表达式的基本语法和自定义函数式接口

    1.写在前面 目前我们学习Java主要用到的应该就是Java8了,或者说大部分企业当前使用的也是Java8.那么既然Java8的应用如此之广泛,一定有一些亮点所在: Lambda 表达式 函数式接口 方法引用与构造器引用 Stream API 接口中的默认方法与静态方法 新时间日期API 其他新特性 速度更快.代码更少(增加了新的语法 Lambda 表达式).强大的 Stream API.便于并行.最大化减少空指针异常 Optional. 2.为什么要使用Lambda表达式? Lambda 是一

  • Java8新特性之Lambda表达式浅析

    说到java 8,首先会想到lambda(闭包)以及虚拟扩展方法(default method),这个特性早已经被各大技术网站炒得沸沸扬扬了,也是我们java 8系列开篇要讲的第一特性(JEP126 http://openjdk.java.net/jeps/126),jdk8的一些库已经应用了lambda表达式重新设计了,理解他对学习java 8新特性有着重要的意义. 一.函数式接口 函数式接口(functional interface 也叫功能性接口,其实是同一个东西).简单来说,函数式接口是

  • 浅析C# 9.0 新特性之 Lambda 弃元参数

    大家好,这是 C# 9.0 新特性短系列的第 5 篇文章. 弃元(Discards) 是在 C# 7.0 的时候开始支持的,它是一种人为丢弃不使用的临时虚拟变量.语法上它是用来赋值的,但它却不被分配存储空间,即没有值,所以不能从中读取值.弃元用 _(下划线) 表示,下划线是一个关键字,只能赋值,不能读取,例如: 在 C# 7.0 中,弃元的使用场景主要有下面四种: 元组和对象的解构 使用 is 和 switch 的模式匹配 对具有 out 参数的方法的调用 作用域内独立使用场景 针对这几个场景,

  • 详解c++11新特性之模板的改进

    C++11关于模板有一些细节的改进: 模板的右尖括号 模板的别名 函数模板的默认模板参数 模板的右尖括号 C++11之前是不允许两个右尖括号出现的,会被认为是右移操作符,所以需要中间加个空格进行分割,避免发生编译错误. int main() { std::vector<std::vector<int>> a; // error std::vector<std::vector<int> > b; // ok } 这个我之前都不知道,我开始学编程的时候就已经是C

随机推荐