• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > C++ 中的Lambda表达式写法

C++ 中的Lambda表达式写法

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了c++11 lambda表达式,lambda表达式c++,c++11 lambda,c++ lambda,lambda表达式等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

小喵的唠叨话:

寒假之后,小喵在家里无所事事,最近用C++写代码的时候,用到了std::sort这个函数,每次用这个函数,小喵似乎都得查一下lambda表达式的写法。正好最近很闲,不如总结一下。

在Bing上搜索 C++ lambda ,第一条记录就是MSDN上的C++ lambda的介绍。本文也是基于这篇文章来写的。

那么接下来,我们分几个部分来介绍。

一、什么是Lambda表达式

MSDN上对lambda表达式的解释:

在 C++ 11 中,lambda 表达式(通常称为 “lambda”)是一种在被调用的位置或作为参数传递给函数的位置定义匿名函数对象的简便方法。 Lambda 通常用于封装传递给算法或异步方法的少量代码行。 [1]

看了这个解释,相信大家已经理解lambda表达式是什么。简而言之,lambda表达式就是一种定义函数的简单的方法。

举一个简单的例子:求一个数的阶乘。

这是一般的函数的写法:

// 这里要求n>=0,同时n的取值不能太大,会溢出
// 为了方便,这里并没有处理上面说到的问题
int factorial(int n) {
 int fact = 1;
 for (int i = 1; i <= n; ++ i) fact *= i;
 return fact;
}
</div>

Lambda表达式的写法:

autofactorial = [](int n) {
 int fact = 1;
 for (int i = 1; i <= n; ++ i) fact *= i;
 return fact;
};
</div>

乍一看,这两种定义方式十分的相似。但其实这是两种完全不同的方式,前一种是函数定义式,而后一种是一个表达式。factorial是变量名,等于号后面的是值,也就是一个lambda表达式,本质上是一个匿名的函数。最终factorial就是一个函数。

很多时候,我们只是直接书写lambda表达式,而不需要给他一个名字。比如排序的时候,sort可以接受一个自定义的比较函数,这时候直接书写lambda表达式即可。

二、Lambda表达式的作用

由于lambda本身其实也就是一种函数的定义方式。因此它的主要作用还是和一般函数一样。但是lambda表达式相对于一般函数,又有一些功能之外的作用。参考了知乎上的一些回答 [2] ,小喵也进行了总结。

1、可以用表达式来定义函数,这样使得函数的定义和调用在一起,语意和逻辑上更为紧凑。同时,对于只是用一次的短小的函数,直接调用匿名的lambda表达式是最好的选择,这样就不需要给每个函数起名字了。 /* 起名字一直是一个很令人头疼的问题 */

2、闭包(Closure)。这个小喵的写javascript的时候时常会用到。闭包本质上就是能够访问上下文环境中变量的代码块。

这里我们简单的举个例子,还是之前的求阶乘的问题,现在我们有些提高需求。

现在需要完成下面的三种阶乘的运算:

n! = n * (n – 1) * (n – 2) * …

n!! = n * (n – 2) * (n – 4) * …

n!!! = n * (n – 3) * (n – 6) * …

要求编写3个函数,分别完成上述3种计算。

使用一般的方式写很容易实现,我们这里直接使用lambda表达式来实现:

#include <iostream>
#include <functional>
std::function<int(int)> getFactorialFunc(int n) {
 return [n](int x) {
  int fact = 1;
  for (; x >= 1; x -= n) fact *= x;
  return fact;
 };
}
int main() {
 // 构造要求的三个函数
 autofactorial1 = getFactorialFunc(1);
 autofactorial2 = getFactorialFunc(2);
 autofactorial3 = getFactorialFunc(3);
 // 调用
 std::cout << factorial1(10) << std::endl;
 std::cout << factorial2(10) << std::endl;
 std::cout << factorial3(10) << std::endl;
}
</div>

编译的时候要注意,lambda表达式是C++11开始支持的,所以需要指定一下C++的版本。

g++ factorial_lambda.cpp -o factorial_lambda.out --std=c++11
</div>

运行之后的结果为:

./factorial_lambda.out
3628800
3840
280

这里作为返回值的lambda表达式,可以访问先前传入的参数,这也就是闭包。具体的语法,我们后面会讲到。

3、柯里化(Currying)。这部分小喵也是第一次接触,维基百科有如下解释:

在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。 [3]

下面给出一个例子(也是实现之前的阶乘):

#include <iostream>
#include <functional>
// 两个参数的阶乘
int factorial(int n, int step) {
 int r = 1;
 for (; n >= 1; n -= step) {
  r *= n;
 }
 return r;
}
// curring化的阶乘
std::function<int(int)> currying_factorial(int step) {
 return [step](int n) {
  return factorial(n, step);
 };
}
int main() {
 // 调用普通函数
 std::cout << factorial(10, 1) << std::endl;
 std::cout << factorial(10, 2) << std::endl;
 std::cout << factorial(10, 3) << std::endl;
 // 调用currying函数
 std::cout << currying_factorial(1)(10) << std::endl;
 std::cout << currying_factorial(2)(10) << std::endl;
 std::cout << currying_factorial(3)(10) << std::endl;
 return 0;
}
</div>

4、lambda表达式整体可以被当做函数的参数或者返回值。

闭包和currying的例子就是将整个lambda表达式作为返回值。现在再举一个作为参数的例子:

#include <iostream>
#include <functional>
int operate(int x, int y, const std::function<int(int, int)> &op) {
 return op(x, y);
}
int main() {
 autoadd = [](int x, int y) { return x + y;};
 automul = [](int x, int y) { return x - y;};
 
 std::cout << operate(10, 5, add) << std::endl;
 std::cout << operate(10, 5, mul) << std::endl;
 return 0;
}
</div>

运行的结果:

其实函数也可以当参数传入的(函数指针),但是lambda表达式要更为直观和灵活一些。谁能一眼看出int (*func(int))(int)究竟是什么意思呢(这是一个函数的定义,输入的参数是int,返回值是一个函数指针,函数指针对应的函数的输入和输出类型都是int)。

三、Lambda表达式的语法

看到前面的lambda表达式的各种有趣的功能,现在是不是非常迫切的想尝试一把?

ISO C++ 标准展示了作为第三个参数传递给 std::sort() 函数的简单 lambda:

#include <algorithm> 
#include <cmath> 
 
void abssort(float* x, unsigned n) { 
 std::sort(x, x + n, 
  // Lambda expression begins 
  [](float a, float b) { 
   return (std::abs(a) < std::abs(b)); 
  } // end of lambda expression 
 ); 
}
</div>

lambda表达式的组成部分见下图:

 

Capture 子句(在 C++ 规范中也称为 lambda 引导。)

参数列表(可选)。 (也称为 lambda 声明符)

可变规范(可选)。

异常规范(可选)。

尾随返回类型(可选)。

“lambda 体”

接下来我们需要学习这6个部分。

1、Capture 子句

我们知道,一般情况下,函数只能访问自己的参数和外部的全局变量。而lambda表达式却可以访问上下文的变量(参见闭包的例子)。那么如何指定要访问的变量,以及访问的方式(值或者引用)呢?这就是Capture 子句要解决的问题。

Lambda 可在其主体中引入新的变量(用 C++14),它还可以访问(或 “捕获” )周边范围内的变量。 Lambda 以 Capture 子句(标准语法中的  lambda 引导 )开头,它指定要捕获的变量以及是通过值还是引用进行捕获。 有与号 ( & ) 前缀的变量通过引用访问,没有该前缀的变量通过值访问。

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

可以使用默认捕获模式(标准语法中的 capture-default )来指示如何捕获 lambda 中引用的任何外部变量:[&] 表示通过引用捕获引用的所有变量,而 [=] 表示通过值捕获它们。 可以使用默认捕获模式,然后为特定变量显式指定相反的模式。 例如,如果 lambda 体通过引用访问外部变量  total 并通过值访问外部变量  factor ,则以下 capture 子句等效:

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

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C++ 中使用lambda代替 unique_ptr 的Deleter的方法
  • 基于C++ Lambda表达式的程序优化
  • C++ 中lambda表达式的编译器实现原理
  • C++ 中的Lambda表达式写法
  • C++11中lambda、std::function和std:bind详解
  • 浅析C++11新特性的Lambda表达式
  • 实例讲解C++编程中lambda表达式的使用
  • C++中的Lambda表达式详解
  • C++实现的一个可以写递归lambda的Y函数

相关文章

  • 2017-12-31数据结构 多关键字排序
  • 2017-05-28基于errno返回值的对应错误码的详细介绍
  • 2017-05-28头文件不宜定义变量的原因全面解析
  • 2017-05-28c++图像处理:24位真彩图颜色变换实例
  • 2017-05-28C语言连接并操作Sedna XML数据库的方法
  • 2017-05-28C++可变参数的函数与模板实例分析
  • 2017-05-28C语言通过深度优先搜索来解电梯问题和N皇后问题的示例
  • 2017-05-28C++ 中静态成员函数与非静态成员函数的区别
  • 2017-05-28C语言kmp算法简单示例和实现原理探究
  • 2017-05-28浅析成员函数和常成员函数的调用

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 指向变量的常指针与指向常变量的指针详细解析
    • c++ 成员函数与非成员函数的抉择
    • 关于C语言中参数的传值问题
    • C++ 设置透明背景图片
    • 深入解析C语言中常数的数据类型
    • C语言线性表的顺序表示与实现实例详解
    • C++实现“隐藏实现,开放接口”的方案
    • 简要对比C语言中的setgid()函数和setregid()函数
    • 详谈C++何时需要定义赋值/复制构造函数
    • ACE反应器(Reactor)模式的深入分析

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有