关于cpp的lambda匿名函数那些事

关于cpp的lambda匿名函数那些事

熟悉python的同学都知道,python中存在lambda匿名函数,这种函数往往只能完成简单的功能,常用于内嵌函数:如map()filter()sorted() 等高阶函数。举个例子:

# 使用 lambda 表达式
add = lambda x, y: x + y

# 调用 lambda 函数
print(add(5, 3))  # 输出 8

C++ 中的匿名函数(也称为lambda表达式)是一种可以定义内联函数的方式,它不需要显式地命名函数名称。匿名函数常用于需要传递简单函数的场景,例如回调、算法或事件处理程序。C++C++11 开始引入了 lambda表达式,并在后续版本中不断增强其功能。

1. 基本语法

C++中lambda表达式的基本语法是:

[capture](parameters) -> return_type {function_body};
  • capture:是捕获列表,指定lambda可以捕获的外部作用域的变量,有以下几种常见的捕获方式
    • 按值捕获:通过按值拷贝的方式捕获外部的变量
      • [x]:捕获外部变量x的值
      • [=]:按值捕获所有在lambda中使用的外部变量
    • 按引用捕获:通过引用访问外部变量,可以修改外部变量
      • [&x]:按引用捕获变量 x
      • [&]:按引用捕获所有在 lambda 中使用的外部变量
    • 混合捕获:可以按值和按引用混合捕获
      • [x, &y]:按值捕获 x,按引用捕获 y
    • 捕获所有变量:
      • [=]:按值捕获所有外部变量。
      • [&]:按引用捕获所有外部变量。
  • parameters:是参数列表,类似于普通函数的参数列表
  • return_type:是返回值类型,可以不指定,编译器会自动进行推断
  • function_body:是函数体

一个基本的使用示例:

#include <iostream>

int main() {
    int a = 10;
    int b = 20;

    // 按值捕获
    auto sum = [a, b]() {
        return a + b;
    };
    std::cout << "Sum: " << sum() << std::endl;  // 输出:Sum: 30

    // 按引用捕获
    auto increment = [&a]() {
        a++;
    };
    increment();
    std::cout << "Incremented a: " << a << std::endl;  // 输出:Incremented a: 11

    return 0;
}

2. 使用lambda表达式的常见场景

lambda表达式常用于传递给需要回调函数的库函数,如标准库中的算法函数,如std::for_eachstd::sort等。

举个简单的例子:

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> vec = {3, 1, 4, 1, 5, 9};

    std::sort(vec.begin(), vec.end(), [](int a, int b) {
        return a > b;  // 降序排序
    });

    for (int num : vec) {
        std::cout << num << " ";
    }
    // 输出:9 5 4 3 1 1
    return 0;
}

3. 为什么有了参数列表,还需要捕获列表呢?

有了参数列表,我们可以按照引用来进行参数传递,这些也能够达到捕获列表的目的,为什么还需要捕获列表呢?

捕获列表参数列表在功能上有明显的区别:

  • 捕获列表
    • 用于访问 lambda 定义时所在作用域中的变量,无需显式传递。
    • 可以捕获变量的(按值捕获)或引用(按引用捕获)。
    • 允许 lambda 在不改变函数签名的情况下,访问外部变量。
    • 适用于需要访问多个外部变量,或需要保持与外部变量的关系的场景。
  • 参数列表
    • 用于定义 lambda 接受的输入参数,类似于普通函数的参数。
    • 仅限于通过函数调用传递的参数,无法直接访问外部作用域的变量。
    • 需要显式传递每个需要的变量,适用于那些只依赖于传入参数的场景。

使用lambda表达式可以简化函数签名和调用,当lambda需要访问多个外部变量的时候,使用参数列表会导致函数签名变得复杂,而且调用时需要显式传递所有相关变量,这不仅增加了代码的复杂性,还可能导致错误。

比如:使用std::for_each

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    int a = 5;
    int b = 10;
    std::vector<int> nums = {1, 2, 3, 4, 5};

    // 如果不使用捕获列表,需要显式传递 a 和 b
    std::for_each(nums.begin(), nums.end(), [&](int x, int a_val, int b_val) {
        std::cout << (x + a_val + b_val) << " ";
    }, a, b); // 需要额外传递 a 和 b, 导致代码错误

    return 0;
}

因为std::for_each的标准签名是

template <class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);

这里的 Function 是一个接受一个参数的函数或可调用对象(比如 lambda 表达式)。for_each 依次对范围 [first, last) 中的每个元素调用 f,而 f 只能接受容器中元素作为唯一参数。

在你的代码中,[&](int x, int a_val, int b_val) 这个 lambda 接受三个参数,但 for_each 只能传递一个参数给它,即容器中的元素(x)。因此,传递 abfor_each 调用之后是无效的,编译器会报错,告诉你 for_each 不能传递额外的参数。

如果你想在 lambda 表达式中使用外部变量 ab,你需要通过捕获列表捕获它们,而不是在 for_each 调用后传递它们。捕获列表可以让 lambda 表达式访问定义它时所在作用域中的变量,而不需要改变 lambda 的参数列表或传递额外的参数。如

std::for_each(nums.begin(), nums.end(), [&](int x) {
    std::cout << (x * a + b) << " ";
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值