C++之lambda【匿名函数】

1、语法

语法结构:

cpp 复制代码
[捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 {
	// 函数处理
}

注意:

一般情况下,编译器可以自动推断出lambda表达式的返回类型,所以我们可以不指定返回类型。

但是如果函数体内有多个return语句时,编译器无法自动推断出返回类型,此时必须指定返回类型。

代码:

cpp 复制代码
#include <iostream>

int main(int argc,char **argv)
{
    auto Add = [](float x, float y) -> int {
        return x + y;
    };

    std::cout << Add(1.0, 2.0) << std::endl;
	return 0;
}

结果:

cpp 复制代码
3

2、捕获列表

需要在匿名函数内使用外部变量,可以用捕获列表来传递参数。

2.1、值捕获传递参数

与参数传值类似,值捕获的前提是变量可以拷贝,不同之处则在于,被捕获的变量在 lambda表达式被创建时拷贝,而不是在调用时才拷贝。

代码:

cpp 复制代码
#include <iostream>

int main(int argc,char **argv)
{
    int m = 99;
    int n = 123127;

    auto Add = [m, n](float x, float y) -> int {
        std::cout << m + n + x + y << std::endl;
        return x + y;
    };

    // 修改n的值, 查看Add(1.0, 2.0)打印的值是否发生变化。值捕获的时候不发生变化。
    n = 123;

    std::cout << Add(1.0, 2.0) << std::endl;
	return 0;
}

结果:

cpp 复制代码
123229
3

2.2、引用捕获传递参数

与引用传参类似,引用捕获保存的是引用,值会发生变化。

代码:

cpp 复制代码
#include <iostream>

int main(int argc,char **argv)
{
    int m = 99;
    int n = 123127;

    auto Add = [m, &n](float x, float y) -> int {
        std::cout << m + n + x + y << std::endl;
        return x + y;
    };

    // 修改n的值, 查看Add(1.0, 2.0)打印的值是否发生变化。引用捕获的时候发生变化。
    n = 123;

    std::cout << Add(1.0, 2.0) << std::endl;
	return 0;
}

结果:

cpp 复制代码
225
3

2.3、隐式捕获传递参数

手动书写捕获列表有时候是非常复杂的,这种机械性的工作可以交给编译器来处理,这时候可以在捕获列表中写一个 & 或**=** 向编译器声明采用引用捕获或者值捕获。编译器会将外部变量全部捕获。

代码:

cpp 复制代码
#include <iostream>

void test_1()
{
    int m = 99;
    int n = 123127;

    auto Add = [=](float x, float y) -> int {
        std::cout << m + n + x + y << std::endl;
        return x + y;
    };

    // 修改n的值, 查看Add(1.0, 2.0)打印的值是否发生变化。值捕获的时候不发生变化。
    n = 123;

    std::cout << Add(1.0, 2.0) << std::endl;
}

void test_2()
{
    int m = 99;
    int n = 123127;

    auto Add = [&](float x, float y) -> int {
        std::cout << m + n + x + y << std::endl;
        return x + y;
    };

    // 修改n的值, 查看Add(1.0, 2.0)打印的值是否发生变化。引用捕获的时候发生变化。
    n = 123;

    std::cout << Add(1.0, 2.0) << std::endl;
}

int main(int argc,char **argv)
{
    test_1();
    test_2();
	return 0;
}

结果:

cpp 复制代码
123229
3
225
3

2.4、空捕获列表

捕获列表 [] 为空,表示Lambda不能使用所在函数中的变量。

代码:

cpp 复制代码
#include <iostream>

int main(int argc,char **argv)
{
    int m = 99;
    int n = 123127;

    auto Add = [](float x, float y) -> int {
        // std::cout << m + n + x + y << std::endl;   // 编译失败,不能使用m和n变量
        return x + y;
    };

    // 修改n的值, 查看Add(1.0, 2.0)打印的值是否发生变化。值捕获的时候不发生变化。
    n = 123;

    std::cout << Add(1.0, 2.0) << std::endl;
	return 0;
}

结果:

cpp 复制代码
   

2.5、表达式捕获

上面提到的值捕获、引用捕获都是已经在外层作用域声明的变量,因此这些捕获方式捕获的均为左值,而不能捕获右值。

C++14之后支持捕获右值,允许捕获的成员用任意的表达式进行初始化,被声明的捕获变量类型会根据表达式进行判断,判断方式与使用 auto 本质上是相同的:

代码:

cpp 复制代码
#include <iostream>
#include <memory>

int main(int argc,char **argv)
{
    int m = 99;
    int n = 123127;
    auto important = std::make_unique<int>(1);

    auto Add = [mm = 200, nn = std::move(important)](float x, float y) -> int {
        std::cout << mm + *nn + x + y << std::endl;
        return x + y;
    };

    // 修改n的值, 查看Add(1.0, 2.0)打印的值是否发生变化。值捕获的时候不发生变化。
    n = 123;

    std::cout << Add(1.0, 2.0) << std::endl;
	return 0;
}

结果:

cpp 复制代码
204
3

2.6、可变lambda

(1)采用值捕获的方式,lambda不能修改其值,如果想要修改,使用mutable修饰。

(2)采用引用捕获的方式,lambda可以直接修改其值。

代码:

cpp 复制代码
#include <iostream>

void test_1()
{
    // 值传递,加关键字mutable
    int m = 10;
    auto Add = [m](auto x, auto y) mutable -> auto {
        m++;
        return x + y + m;
    };
    std::cout << Add(1, 4) << std::endl;
}

void test_2()
{
    // 引用传递可直接修改
    int m = 10;
    auto Add = [&m](auto x, auto y) -> auto {
        m++;
        return x + y + m;
    };
    std::cout << Add(1, 4) << std::endl;
}

int main(int argc,char **argv)
{
	test_1();
	test_2();
	return 0;
}

结果:

cpp 复制代码
16
16

2.7、混合捕获

(1)要求捕获列表中第一个元素必须是隐式捕获(&或=)。

(2)混合使用时,若隐式捕获采用引用捕获(&)则显式捕获的变量必须采用值捕获的方式。

(3)若隐式捕获采用值捕获(=),则显式捕获的变量必须采用引用捕获的方式。

代码:

cpp 复制代码
#include <iostream>

void test_1()
{
    int m = 99;
    int n = 123127;

    auto Add = [=, &n, m1 = 4.5](float x, float y) -> int {
        std::cout << m + n + x + y + m1 << std::endl;
        return x + y;
    };

    // 修改n的值, 查看Add(1.0, 2.0)打印的值是否发生变化。值捕获的时候不发生变化。
    n = 123;

    std::cout << Add(1.0, 2.0) << std::endl;
}

void test_2()
{
    int m = 99;
    int n = 123127;

    auto Add = [&, n, m1 = 1](float x, float y) -> int {
        std::cout << m + n + x + y + m1 << std::endl;
        return x + y;
    };

    // 修改n的值, 查看Add(1.0, 2.0)打印的值是否发生变化。引用捕获的时候发生变化。
    n = 123;

    std::cout << Add(1.0, 2.0) << std::endl;
}

int main(int argc,char **argv)
{
    test_1();
    test_2();
	return 0;
}

结果:

cpp 复制代码
229.5
3
123230
3

3、泛型 Lambda

在C++14之前,lambda表示的形参只能指定具体的类型,没法泛型化。从 C++14 开始, Lambda 函数的形式参数可以使用 auto关键字来产生意义上的泛型。

代码:

cpp 复制代码
#include <iostream>

int main(int argc,char **argv)
{
    auto Add = [](auto x, auto y) -> auto {
        return x + y;
    };

    std::cout << Add(1.4, 2.0) << std::endl;
    std::cout << Add(1, 2) << std::endl;
	return 0;
}

结果:

cpp 复制代码
3.4
3
相关推荐
Teable任意门互动几秒前
主流多维表格产品深度解析:飞书、Teable、简道云、明道云、WPS
开发语言·网络·开源·钉钉·飞书·开源软件·wps
王哈哈^_^4 分钟前
【数据集】【YOLO】【目标检测】农作物病害数据集 11498 张,病害检测,YOLOv8农作物病虫害识别系统实战训推教程。
人工智能·深度学习·算法·yolo·目标检测·计算机视觉·1024程序员节
xier_ran6 分钟前
邻接矩阵的 k 次幂意味着什么?从图论到路径计数的直观解释
算法·图论
程序员大雄学编程40 分钟前
「用Python来学微积分」16. 导数问题举例
开发语言·python·数学·微积分
B站_计算机毕业设计之家1 小时前
预测算法:股票数据分析预测系统 股票预测 股价预测 Arima预测算法(时间序列预测算法) Flask 框架 大数据(源码)✅
python·算法·机器学习·数据分析·flask·股票·预测
Dreams_l1 小时前
redis中的数据类型
java·开发语言
梵得儿SHI1 小时前
Java IO 流详解:字符流(Reader/Writer)与字符编码那些事
java·开发语言·字符编码·工作原理·字符流·处理文本
太过平凡的小蚂蚁1 小时前
Kotlin 协程中常见的异步返回与控制方式(速览)
开发语言·前端·kotlin
007php0072 小时前
京东面试题解析:同步方法、线程池、Spring、Dubbo、消息队列、Redis等
开发语言·后端·百度·面试·职场和发展·架构·1024程序员节
想唱rap2 小时前
C++ list 类的使用
c语言·开发语言·数据结构·c++·笔记·算法·list