lambda 表达式

关于C++Lambda表达式的学习记录

在刷到 LeetCode189:轮转数组的地方,使用 reverse 解决这个问题很方便, 然后看题解使用了Lambda表达式,由于我对C++不是很熟,使用AI学习一下。

下面是题解。

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int size = nums.size();
        auto reverse = [&] (int i, int j) {
            while(i < j) {
                swap(nums[i++], nums[j--]);
            }
        };
        k %= size;
        reverse(0, size-1);
        reverse(0, k-1);
        reverse(k, size-1);
    }
};

一、整体语法拆解

先看完整结构:

auto reverse = [&](int i, int j){
    while(i < j){
        swap(nums[i++], nums[j--]);
    }
};

我们把它拆成 5 个核心部分:

1. auto reverse:变量声明

  • auto:C++11 引入的自动类型推导,编译器会根据右侧 lambda 表达式的类型,自动推导 reverse 的类型(本质是 lambda 的闭包类型)。
  • reverse:给这个匿名函数起的 “名字”,后续可以像调用普通函数一样用 reverse(i, j) 调用。

2. [&]:lambda 捕获子句(捕获列表)

这是 lambda 最关键的部分之一,决定了 lambda 内部能访问哪些外部变量,以及访问方式:

  • [&]:表示以引用方式捕获所有外部可见的变量(即 lambda 定义所在作用域的变量)。

  • 这里能直接用 numsswapswap 是全局 / 命名空间函数),就是因为 [&] 捕获了外部的 nums 变量(比如外层函数的 vector / 数组)。

  • 补充捕获方式(常用):

    写法 含义
    [] 不捕获任何外部变量(空捕获)
    [=] 以值拷贝方式捕获所有外部变量
    [&x] 只以引用捕获变量 x
    [x] 只以值拷贝捕获变量 x
    [&,x] 引用捕获所有变量,唯独 x 按值捕获

3. (int i, int j):lambda 参数列表

和普通函数的参数列表完全一致:

  • 声明了两个整型参数 ij,表示要反转的区间的起始索引结束索引(闭区间 [i, j])。
  • 比如调用 reverse(0, 4),就是反转数组索引 0 到 4 的元素。

4. { ... }:lambda 函数体

核心逻辑是反转区间 [i, j] 的元素:

while(i < j){
    swap(nums[i++], nums[j--]);
}
  • 循环条件 i < j:保证只交换到区间中间(避免重复交换)。
  • swap(nums[i++], nums[j--])
    • swap:交换两个元素(C++ 标准库函数,需包含 <algorithm>,或自定义)。
    • i++/j--:交换后,起始索引右移、结束索引左移,直到相遇。

5. 分号 ;:语句结束

lambda 表达式是一个 “表达式”,但赋值给变量时,整体是一条语句,必须以分号结尾。

二、核心特性:lambda 表达式的本质

lambda 是 C++11 引入的匿名函数,本质是:

  1. 编译器会把 lambda 翻译成一个匿名类(闭包类),并创建该类的一个临时对象(闭包对象)。
  2. auto reverse 实际是把这个闭包对象赋值给变量 reverse
  3. 调用 reverse(i, j) 等价于调用闭包类的 operator() 成员函数。

三、使用示例

结合完整场景,比如反转数组的某段区间:

#include <iostream>
#include <vector>
#include <algorithm> // swap 需包含此头文件

using namespace std;

int main() {
    vector<int> nums = {1,2,3,4,5};
    
    // 定义 lambda 函数
    auto reverse = [&](int i, int j){
        while(i < j){
            swap(nums[i++], nums[j--]);
        }
    };
    
    // 调用:反转索引 1 到 3 的元素(2,3,4 → 4,3,2)
    reverse(1, 3);
    
    // 输出结果:1 4 3 2 5
    for(int num : nums) {
        cout << num << " ";
    }
    
    return 0;
}

四、关键注意事项

  1. 捕获的生命周期

    • 因为用 [&] 引用捕获 nums,所以 reverse 的生命周期不能超过 nums(比如 nums 销毁后调用 reverse 会导致野引用)。
    • 如果想避免这个问题,可以用 [=] 值捕获,但 swap 会修改拷贝后的 nums,原数组不会变(按需选择)。
  2. lambda 的可调用性

    • 无捕获的 lambda([])可以隐式转换为函数指针,有捕获的不行。
    • 本例有捕获,只能通过变量 reverse 调用。
  3. 参数和返回值

    • 本例 lambda 无返回值(默认 void),如果需要返回值,可显式指定:

      auto add = [](int a, int b) -> int { return a + b; };
      

五、为什么用这种写法?

  • 简洁:无需单独定义一个全局 / 成员函数,就地定义就地使用。
  • 闭包特性:自动捕获外部变量,无需手动传参(比如本例不用把 nums 作为参数传入 reverse)。
  • 灵活:常用于算法场景(比如排序的自定义比较、局部的小功能函数)。
posted @ 2025-12-10 14:18  BuerH  阅读(0)  评论(0)    收藏  举报