什么是Lambda?

C++ 11增加了一个很重要的特性——Lambda表达式。营里(戴维营)的兄弟都对Objective-C很熟悉,很多人多block情有独钟,将各种回调函数、代理通通都用它来实现。甚至有人选择用FBKVOController、BlocksKit等开源框架将KVO、控件事件处理都改为通过block解决。原因就是简单、方便、直观,函数的定义和使用出如今同一个地方。这里的Lambda表达式实际上和block很类似,当然假设你用它和Swift语言的闭包比較,那就是一回事了。以下先看几个Lambda和block的演示样例代码。

1 .Objective-C的block演示样例代码,使用^表示block类型,整体来说与函数指针的定义类似。

  1. #import <Foundation/Foundation.h>
  2. int main()
  3. {
  4. void (^block)() = ^void() {
  5. NSLog(@"In block");
  6. };
  7. block();
  8. return 0;
  9. }

编译执行:

  1. $ clang main.m -framework Foundation
  2. $ ./a.out
  3. 2015-01-28 14:17:52.763 a.out[9901:165707] In block

2 .Swift的闭包,參数列表、返回值类型等都写在花括号{}内部。

  1. let closure = {
  2. () -> Void in
  3. println("In swift")
  4. }
  5. closure()

编译执行:

  1. $ swiftc main.swift
  2. $ ./main
  3. In swift

測试Swift也能够直接使用Playground或REPL(Read-Eval-Print-Loop)环境。

3 .C++的Lambda表达式。

  1. #include <iostream>
  2. int main()
  3. {
  4. auto lambda = []() -> void{
  5. std::cout << "In lambda" << std::endl;
  6. };
  7. lambda();
  8. return 0;
  9. }

编译执行,注意使用C++ 11的特性进行编译:

  1. $ clang++ main.cpp -std=c++11
  2. $ ./a.out
  3. In lambda

Lambda、block实际上都是一个闭包(closure),它们都类似于一个匿名的函数,可是拥有捕获所在作用域中变量的能力;可以将函数做为对象一样使用。通经常使用它们来实现回调函数、代理等功能。

Lambda用法

1 .基本形式

语法 序号
[ 捕获列表
] ( 形參数列表 ) mutable(可选) 异常属性 -> 返回值类型 { 函数体 }
(1)
[ capture-list
] ( params ) -> ret { body }
(2)
[ capture-list
] ( params ) { body }
(3)
[ capture-list
] { body }
(4)
  • (1)为完整的形式,包括变量捕获列表、形參列表、可变属性(可选)和返回值类型等。
  • (2)省略了mutable,表示Lambda不能改动捕获的变量。
  • Lambda的返回值类型假设能够由函数体中的实际返回值推导出,能够省略。
  • 假设没有形參,能够省略圆括号。

2 .捕获列表

lambda表达式中能够获取(捕获)它所在作用域中的变量值,而且有两种捕获方式:引用和值。我们能够在捕获列表中设置各变量的捕获方式。假设没有设置捕获列表,lambda默认不能捕获不论什么的变量,这点与block不同。

  1. #include <iostream>
  2. int main()
  3. {
  4. int a = 123;
  5. auto lambda = []()->void{
  6. std::cout << "In lambda: " << a << std::endl;
  7. };
  8. lambda();
  9. return 0;
  10. }

编译执行:

  1. $ clang++ main.cpp -std=c++11
  2. main.cpp:8:33: error: variable 'a' cannot be implicitly captured in a lambda
  3. with no capture-default specified
  4. std::cout << "In lambda: " << a << std::endl;
  5. ^
  6. main.cpp:5:6: note: 'a' declared here
  7. int a = 123;
  8. ^
  9. main.cpp:7:16: note: lambda expression begins here
  10. auto lambda = []()->void{
  11. ^
  12. 1 error generated.

[]中设置捕获列表,就能够在lambda中使用变量a了,这里使用按值(=
by value)捕获。

  1. #include <iostream>
  2. int main()
  3. {
  4. int a = 123;
  5. auto lambda = [=]()->void{
  6. std::cout << "In lambda: " << a << std::endl;
  7. };
  8. lambda();
  9. return 0;
  10. }

编译执行:

  1. $ clang++ main.cpp -std=c++11
  2. $ ./a.out
  3. In lambda: 123

捕获列表的设置方式:

|设置方式|结果| |-|-| |[]|不捕获| |[=]|所有按值捕获| |[&]|所有按引用捕获| |[a, b]|按值捕获变量a和b| |[&a, b]|按引用捕获a,按值捕获b| |[&, a]|按值捕获a,其他变量所有按引用捕获| |[=, &a]|按引用捕获a,其他所有按值捕获|

注意: 捕获列表没有先后顺序;捕获列表中的变量不能出现反复申明,比方[&,
&a]
&中已经包括了&a的申明了,因此不能再出现&a

详细的捕获类型,能够通过打印变量地址进行查看。

  • 按值捕获:
  1. auto lambda = [=]()->void{
  2. std::cout << "In lambda: " << &a << std::endl;
  3. };

执行结果为:

  1. $ clang++ main.cpp -std=c++11
  2. $ ./a.out
  3. 0x7fff555c69b8
  4. In lambda: 0x7fff555c69b0
  • 按引用捕获:
  1. auto lambda = [&]()->void{
  2. std::cout << "In lambda: " << &a << std::endl;
  3. };

执行结果为:

  1. $ clang++ main.cpp -std=c++11
  2. $ ./a.out
  3. 0x7fff58a9b9b8
  4. In lambda: 0x7fff58a9b9b8

3 .可变类型(mutable)

按值传递到lambda中的变量,默认是不可变的(immutable),假设须要在lambda中进行改动的话,须要在形參列表后加入mutablekeyword(按值传递无法改变lambda外变量的值)。

  1. #include <iostream>
  2. int main()
  3. {
  4. int a = 123;
  5. std::cout << a << std::endl;
  6. auto lambda = [=]() mutable ->void{
  7. a = 234;
  8. std::cout << "In lambda: " << a << std::endl;
  9. };
  10. lambda();
  11. std::cout << a << std::endl;
  12. return 0;
  13. }

编译执行结果为:

  1. $ clang++ main.cpp -std=c++11
  2. lishan:c_study apple$ ./a.out
  3. 123
  4. In lambda: 234 #能够改动
  5. 123 #注意这里的值,并没有改变

假设没有加入mutable,则编译出错:

  1. $ clang++ main.cpp -std=c++11
  2. main.cpp:9:5: error: cannot assign to a variable captured by copy in a
  3. non-mutable lambda
  4. a = 234;
  5. ~ ^
  6. 1 error generated.

4 .返回值类型。

lambda的返回值类型能够省略,编译器会依据实际返回值的类型自己主动推导。

  1. #include <iostream>
  2. int main()
  3. {
  4. int a = 123;
  5. std::cout << a << std::endl;
  6. auto lambda = [=]() mutable{
  7. a = 234;
  8. std::cout << "In lambda: " << a << std::endl;
  9. return a;
  10. };
  11. int b = lambda();
  12. std::cout << b << std::endl;
  13. return 0;
  14. }

编译执行:

  1. $ clang++ main.cpp -std=c++11
  2. $ ./a.out
  3. 123
  4. In lambda: 234
  5. 234

參考资料

  1. https://msdn.microsoft.com/en-us/library/dd293603.aspx
  2. http://www.oracle.com/technetwork/articles/servers-storage-dev/howto-use-lambda-exp-cpp11-2189895.html
  3. http://en.cppreference.com/w/cpp/language/lambda
  4. http://www.cprogramming.com/c++11/c++11-lambda-closures.html

本文档由长沙戴维营教育整理。

C++教程之lambda表达式一的更多相关文章

  1. Python函数式编程之lambda表达式

    一:匿名函数的定义 lambda parameter_list: expression 二:三元表达式 条件为真时返回的结果 if 条件判断 else 条件为假的时候返回的结果 三:map map(f ...

  2. java函数式编程之lambda表达式

    作为比较老牌的面向对象的编程语言java,在对函数式编程的支持上一直不温不火. 认为面向对象式编程就应该纯粹的面向对象,于是经常看到这样的写法:如果你想写一个方法,那么就必须把它放到一个类里面,然后n ...

  3. 12_Python的(匿名函数)Lambda表达式_Python编程之路

    Python作为一门高级语言,与很多编程语言一样都具有匿名函数这一特征 匿名函数,也就Lambda表达式,通俗来讲就是不用命名的方法,直接定义,直接用即可 创建匿名函数需要用到Lambda关键字,下面 ...

  4. Pandas高级教程之:GroupBy用法

    Pandas高级教程之:GroupBy用法 目录 简介 分割数据 多index get_group dropna groups属性 index的层级 group的遍历 聚合操作 通用聚合方法 同时使用 ...

  5. CRL快速开发框架系列教程二(基于Lambda表达式查询)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  6. Tkinter教程之Text(1)篇

    本文转载自:http://blog.csdn.net/jcodeer/article/details/1811343 '''Tkinter教程之Text篇(1)''''''1.创建第一个Text''' ...

  7. Java8一:Lambda表达式教程

    1. 什么是λ表达式 λ表达式本质上是一个匿名方法.让我们来看下面这个例子: public int add(int x, int y) {         return x + y;     } 转成 ...

  8. Python爬虫与数据分析之进阶教程:文件操作、lambda表达式、递归、yield生成器

    专栏目录: Python爬虫与数据分析之python教学视频.python源码分享,python Python爬虫与数据分析之基础教程:Python的语法.字典.元组.列表 Python爬虫与数据分析 ...

  9. λ(lambda)表达式

    理论阶段 函数接口 函数接口是行为的抽象: 函数接口是数据转换器; java.util.Function包.定义了四个最基础的函数接口: Supplier<T>: 数据提供器,可以提供 T ...

随机推荐

  1. VS2010关于error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏

    前段时间自己的系统一直在安装更新.今天突然打开VS2010当运行的时候一直出现error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏这种错误.然后就百度 解决的方法: 1.项目\属 ...

  2. UVa 11408 - Count DePrimes

    题目:一个数的素因子的和假设也是素数就叫做DePrimes,统计给定区间内的DePrimes. 分析:数论.本题使用用一种素数的筛法,欧拉筛法,也加线性筛法. 这样的方法,每次删选分两种情况:1.素因 ...

  3. KVC该机制

    KVC该机制 KVC是cocoa的大招,用来间接获取或者改动对象属性的方式. 一.KVC的作用: KVC大招之中的一个: [self setValuesForKeysWithDictionary:di ...

  4. arcmap坐标点生成线和面(更正版)

    一:本博客的脉络 (1 )做了例如以下更正:之前在网上搜到的结果是:arcmap坐标点生成线和面 ------ 注意该功能在ArcGIS10中没有了,当时自己也没有多想就转载了,再此做一下更正或者叫做 ...

  5. poj 2253 Frogger (最长路中的最短路)

    链接:poj 2253 题意:给出青蛙A,B和若干石头的坐标,现青蛙A想到青蛙B那,A可通过随意石头到达B, 问从A到B多条路径中的最长边中的最短距离 分析:这题是最短路的变形,曾经求的是路径总长的最 ...

  6. HDU1698_Just a Hook(线段树/成段更新)

    解题报告 题意: 原本区间1到n都是1,区间成段改变成一个值,求最后区间1到n的和. 思路: 线段树成段更新,区间去和. #include <iostream> #include < ...

  7. SQLServer 网络协议(一)

    SQLserver现在主要的3种协议:Shared Memory.TCP/IP 和 Named Pipe SharedMemory: Shared Memory最快最简单的协议,使用SharedMem ...

  8. php编码规范个人小结

    1.原生态代码或者类的头上,注明作者,关键算法计算过程 例如 /** *@author zengmoumou *功能:根据列表ip,取得ip对应的运营商,省,市,县 */ 2.变量尽量用英文单词的组合 ...

  9. Sliverlight之 矢量绘图

    目标:在两天内完成一个环形图的绘制 准备:第5章 矢量绘图 1,形状绘图(见Project11) (1)线条用什么标签表示,它有哪几个重要属性 说明: Line标签 x1 y1表示起始点x,y坐标 x ...

  10. 重新想象 Windows 8 Store Apps (21) - 动画: ThemeTransition(过渡效果)

    原文:重新想象 Windows 8 Store Apps (21) - 动画: ThemeTransition(过渡效果) [源码下载] 重新想象 Windows 8 Store Apps (21) ...