一、前言

由于前段时间在阅读一些C++源码的时候发现了Lambda表达式,所以在此也记录下Lambda表达式的使用。

很早之前Lambda在很多高级语言中,就已经被广泛地使用了,在一个程序中Lambda表达式可以理解为是匿名函数。在C++中,到了C++11标准才引入了这个Lambda表达式,这是C++11最重要而且也是最常用的特性之一。

使用Lambda表达式,不需要额外地定义函数名,可以更直接编写程序,有比较好的可读性和可维护性;不需要另外声明和定义函数体,避免了程序代码的膨胀。

二、Lambda表达式格式说明

2.1 完整的Lambda表达式格式

[capture list] (params list) mutable exception-> return type { function body }

说明:

名称 解析
[capture list] 捕获列表:lambda 表达式可以通过捕获列表捕获一定范围内的变量。
(params list) 形参列表,用于传参(可以省略)。
mutable 用来说明是否可以修改按值捕获的变量(可以省略),如果需要修改按值捕获的变量,则需要添加。
exception 异常设定(可以省略)。
return type 返回类型 (可省略,如果省略则自动从函数体中判断返回类型,return后的值。如果没有则返回void)。
function body 函数体,即逻辑代码。

2.2 常见的Lambda表达式格式

编号 格式 特性
格式1 [capture list] (params list) -> return type {function body} 1、无法修改捕获列表中的变量值。
格式2 [capture list] (params list) {function body} 1、无法修改捕获列表中的变量值;2、返回类型由return返回的值类型确定,如果没有return语句,则返回类型为void。
格式3 [capture list] {function body} 1、无法修改捕获列表中的变量值;2、返回类型由return返回的值类型确定,如果没有return语句,则返回类型为void;3、不能传入参数,类似普通的无参函数。

2.3 lambda 表达式捕获列表

捕获形式 解析
[ ] 不捕获任何变量。
[&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
[=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。
[=,&x] 按值捕获外部作用域中所有变量,并按引用捕获 x 变量。
[x] 按值捕获 x 变量,同时不捕获其他变量。
[this] 捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员函数同样的访问权限。如果已经使用了 & 或者 =,就默认添加此选项。

注意:

  • 如果是按值捕获,那么是否可以改变捕获的变量值,取决于mutable关键字。

三、示例

3.1 STL的sort函数参数使用Lambda

/*****************************************************************************
** Copyright 2020 lcg. All rights reserved.
** File name: Lambda.cpp
** Description: 在STL的sort函数参数使用Lambda表达式
** Author: lcg
** Version: 1.0
** Date: 2020.12.04
*****************************************************************************/ #include <iostream>
#include <vector>
#include <algorithm>
using namespace std; bool cmp(int a, int b)
{
return a < b;
} int main()
{
vector<int> vec{ 3, 2, 5, 7, 3, 2 };
vector<int> lbvec(vec); /**1、不使用Lambda表达式的写法**/
sort(vec.begin(), vec.end(), cmp);
cout << "predicate function:" << endl;
for (int it : vec) // 此for循环写法也是在C++11才出现
cout << it << ' ';
cout << endl; /**2、不使用Lambda表达式的写法**/
sort(lbvec.begin(), lbvec.end(), [](int a, int b) -> bool { return a < b; });
cout << "lambda expression:" << endl;
for (int it : lbvec)
cout << it << ' ';
}

可以看到这种情况使用Lambda表达式可以使代码更加直观、简介,无需再定义 cmp(int a, int b) 函数。

3.2 有返回值的Lambda表达式

/** 1、标明返回类型**/
auto f = [](int a) -> int { return a + 1; };
std::cout << f(1) << std::endl; /**输出: 2**/ /** 2、无标明返回类型**/
auto f = [](int a) { return a + 1; };
std::cout << f(1) << std::endl; /**输出: 2**/

当没有标明返回类型的时候,系统会根据return回来的值来判断返回值的类型,auto会自动检索返回值的类型。

3.3 无参数Lambda表达式

auto f = []() { return 1; };
std::cout << f() << std::endl; /**输出: 1**/

3.4 捕获外部变量的Lambda表达式

在类中使用:

/*****************************************************************************
** Copyright 2020 lcg. All rights reserved.
** File name: Lambda.cpp
** Description: 捕获外部变量的Lambda表达式
** Author: lcg
** Version: 1.0
** Date: 2020.12.04
*****************************************************************************/ #include <iostream> class A
{
public:
int i_ = 0;
void func(int x, int y)
{
/* error,没有捕获外部变量*/
auto x1 = []{ return i_; }; /*OK,按值捕获所有外部变量,包括了this指针*/
auto x2 = [=]{ return i_ + x + y; }; /*OK,按引用捕获所有外部变量,包括了this指针*/
auto x3 = [&]{ return i_ + x + y; }; /*OK,捕获this指针,Lambda拥有和此类中普通函数一样的权限*/
auto x4 = [this]{ return i_; }; /*error,没有捕获x、y,因为x、y变量不属于this*/
auto x5 = [this]{ return i_ + x + y; }; /* OK,捕获this指针、x、y*/
auto x6 = [this, x, y]{ return i_ + x + y; }; /*OK,捕获this指针,并修改成员的值*/
auto x7 = [this]{ return i_=7; };
x7();
std::cout<<i_<<std::endl;//输出7 /*OK,捕获所有外部变量,默认捕获this指针,并修改成员的值*/
auto x8 = [=]{ return i_=8; };
x8();
std::cout<<i_<<std::endl;//输出8 /*error,因为i_不属于捕获范围的变量,所以无法按值捕获i_变量,可以通过捕获this指针来获取i_使用权*/
auto x9 = [i_]{ return i_++; }; /*error,原因同上*/
auto x10 = [&i_]{ return i_++; }; /*ok,按引用捕获所有变量,默认捕获this指针,并修改成员的值*/
auto x11 = [&]{ return i_=11; };
x11();
std::cout<<i_<<std::endl;//输出11 /*error,按值捕获x变量,并修改值*/
auto x12 = [x]{ return x++; }; /*ok,按值捕获x变量,并修改值*/
auto x13 = [x]()mutable{ return x=13; };
x13();
std::cout<<x<<std::endl;//输出1 /*ok,按引用捕获x变量,并修改值*/
auto x14 = [&x]{ return x=14; };
x14();
std::cout<<x<<std::endl;//输出14
}
}; int main(int argc, char *argv[])
{
A l;
l.func(1,2); int a = 0, b = 1;
/*error,没有捕获外部变量*/
auto f1 = []{ return a; }; /*OK,捕获所有外部变量,改变a值*/
auto f2 = [&]{ return a=2; };
f2();
std::cout<<a<<std::endl;//输出2 /*OK,捕获所有外部变量,并返回a*/
auto f3 = [=]{ return a; }; /*error,a是以复制方式捕获的,无法修改*/
auto f4 = [=]{ return a=4; }; /*ok,a是以复制方式捕获的,修改a值*/
auto f4 = [=]()mutable{ return a=4; };
f4();
std::cout<<a<<std::endl;//输出2 /*error,没有捕获变量b*/
auto f5 = [a]{ return a + b; }; /*OK,捕获a和b的引用,并对b做自加运算*/
auto f6 = [a, &b]{ return a + (b++); }; /*OK,捕获所有外部变量和b的引用,并对b做自加运算*/
auto f7 = [=, &b]{ return a + (b++); }; return 0;
}

总结:

  • 当按值的方式获取外部变量时,是无法更改获取过来的值的,除非使用mutable关键字声明,就可以更改(更改的不是外部变量原本地址里的值,而是lambda函数体内的副本);
  • 当按引用的方式捕获外部变量时,lambda函数体内可以更改此值,更改的是外部变量原本地址里的值;
  • 当在类中,lambda表达式捕获this指针时,lambda函数体内可以直接改变该类中的变量,和类中的普通函数拥有一样的权限。

【C++】C++之Lambda表达式的更多相关文章

  1. 你知道C#中的Lambda表达式的演化过程吗?

    那得从很久很久以前说起了,记得那个时候... 懵懂的记得从前有个叫委托的东西是那么的高深难懂. 委托的使用 例一: 什么是委托? 个人理解:用来传递方法的类型.(用来传递数字的类型有int.float ...

  2. Linq表达式、Lambda表达式你更喜欢哪个?

    什么是Linq表达式?什么是Lambda表达式? 如图: 由此可见Linq表达式和Lambda表达式并没有什么可比性. 那与Lambda表达式相关的整条语句称作什么呢?在微软并没有给出官方的命名,在& ...

  3. 背后的故事之 - 快乐的Lambda表达式(一)

    快乐的Lambda表达式(二) 自从Lambda随.NET Framework3.5出现在.NET开发者眼前以来,它已经给我们带来了太多的欣喜.它优雅,对开发者更友好,能提高开发效率,天啊!它还有可能 ...

  4. Kotlin的Lambda表达式以及它们怎样简化Android开发(KAD 07)

    作者:Antonio Leiva 时间:Jan 5, 2017 原文链接:https://antonioleiva.com/lambdas-kotlin/ 由于Lambda表达式允许更简单的方式建模式 ...

  5. java8中lambda表达式的应用,以及一些泛型相关

    语法部分就不写了,我们直接抛出一个实际问题,看看java8的这些新特性究竟能给我们带来哪些便利 顺带用到一些泛型编程,一切都是为了简化代码 场景: 一个数据类,用于记录职工信息 public clas ...

  6. 背后的故事之 - 快乐的Lambda表达式(二)

    快乐的Lambda表达式 上一篇 背后的故事之 - 快乐的Lambda表达式(一)我们由浅入深的分析了一下Lambda表达式.知道了它和委托以及普通方法的区别,并且通过测试对比他们之间的性能,然后我们 ...

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

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

  8. Lambda 表达式递归用法实例

    注意: 使用Lambda表达式会增加额外开销,但却有时候又蛮方便的. Windows下查找子孙窗口实例: HWND FindDescendantWindows(HWND hWndParent, LPC ...

  9. Spark中Lambda表达式的变量作用域

    通常,我们希望能够在lambda表达式的闭合方法或类中访问其他的变量,例如: package java8test; public class T1 { public static void main( ...

  10. 释放Android的函数式能量(I):Kotlin语言的Lambda表达式

    原文标题:Unleash functional power on Android (I): Kotlin lambdas 原文链接:http://antonioleiva.com/operator-o ...

随机推荐

  1. POI做题记录

    嘿嘿,偷学一波! 由于博主做的题比较少,所以没按年份整理,直接按照做题时间放上来了. 2020年9月20日 [POI2013]LUK-Triumphal arch 给你一颗\(n\)个点的树(\(n\ ...

  2. opencv--ORB::create

    static Ptr<ORB> cv::ORB::create (                         int nfeatures = 500,                 ...

  3. ORA-00020: maximum number of processes (40) exceeded模拟会话连接数满

    问题描述:在正式生产环境中,有的库建的process和session连接数目设置的较小,导致后期满了无法连接.因为正式库无法进行停库修改,只能释放连接,做个测试模拟 1. 修改现有最大会话与进程连接数 ...

  4. Centos7上一次War包的部署与运行

    Centos7上一次War包的部署与运行 前言 由于前段时间第一次部署一个小型的项目,时间一长所以有些步骤有时候时间一长就忘了,在此做个简单的记录 一.原始系统开发环境 操作系统:Windows10: ...

  5. 进程 PCB 进程挂起

    7-1 进程定义  OS系统从只能跑一个程序到能跑多个.进程可以描述程序的执行过程. 进程:一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程. 只有当一个程序被OS加载到内存中,cpu对其 ...

  6. JAVA程序员工作常用英语(细心整理)

    基础----进阶 A. array数组accessible 可存取的 area面积audio 音频 addition 加法 action 行动 arithmetic 算法adjustment 调整 a ...

  7. 二:Tomcat与登录

    在使用Tomcat部署服务器之前我们需要写一个页面登录的HTML文本 该文本如下 <!DOCTYPE html> <html> <head> <meta ch ...

  8. 【Python】Json序列化和反序列化模块dumps、loads、dump、load函数介绍

    1.json.dumps()  json.dumps()用于将dict类型的数据转成str,因为如果直接将dict类型的数据写入json文件中会发生报错,因此在将数据写入时需要用到该函数. 转换案例: ...

  9. Windows查找JDK的路径

    第一步:确定是否安装JDK 在控制台输入: java -version 此时说明你电脑安装了JDK. 第二步:查找路径 然后在控制台输入: java -verbose 输出结果: 最后两行就是相应的j ...

  10. Java(6)集合

    一.Java集合框架概述 1.什么是集合 集合框架:用于存储数据的容器. 数组.集合等存储数据的结构,叫Java容器. 此时的存储,是指内存层面的存储,不涉及持久化的存储. 任何集合框架都包含三大块的 ...