Lambda

基本概念

定义

Lambda函数(亦称为Lambda表达式、匿名函数)是一种函数对象(变量),可以让函数像普通变量一样进行赋值、传递、函数返回等操作。

核心优势

  • 简化程序代码:针对一次性使用的函数,无需定义命名的普通函数,减少代码冗余。
  • 支撑函数式编程:让函数具备“可流动”特性,为C++函数式编程模式奠定语法基础。

语法逻辑

完整语法结构

Lambda表达式的完整语法格式如下:

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

各组成部分解析

组成部分 含义 & 注意事项
capture list 外部变量捕获列表,指定Lambda可访问的外部作用域变量,是Lambda的核心特征之一。
params list 形参列表,与普通函数形参列表语法一致;使用mutable时该列表不可省略。
mutable 取消Lambda的常量性,允许在函数体内修改值捕获的外部变量;使用时形参列表不可省略。
exception 异常设定,指定Lambda可能抛出的异常类型(如noexcept表示不抛出任何异常)。
return type 返回类型;可省略,编译器会通过decltype根据函数体返回值自动推断。
function body 函数体,Lambda核心逻辑的实现区域。
Lambda语法结构可视化拆解

核心语法细节(结合示例)

外部变量捕获

Lambda的核心特性是捕获外部作用域变量,主要分为值捕获、引用捕获、隐式捕获等类型,以下是具体示例:

值捕获(默认,只读)

值捕获会拷贝外部变量到Lambda内部,默认情况下Lambda是const函数,无法修改值捕获的变量。

#include <iostream>
using namespace std;

int main(int argc, char const *argv[])
{
    int a = 1;
    int b = 2;

    // [a,b] 以值方式捕获a、b,Lambda内部只读
    int sum = [a, b]()->int{return a+b;}(); 
    cout << "sum: " << sum << endl; // 输出:sum: 3

    return 0;
}
  • 关键说明:
    • 末尾的()是Lambda的调用符号,匿名Lambda可直接调用;也可赋值给具名函数对象后调用:
      // 将匿名Lambda赋值给函数对象f
      auto f = [a, b]()->int{return a+b;};
      int sum = f(); // 调用,效果与直接调用一致
      
引用捕获(可读写)

引用捕获会绑定外部变量的引用,Lambda内部可修改原变量(无需mutable),需注意变量生命周期(避免悬垂引用)。

#include <iostream>
using namespace std;

int main(int argc, char const *argv[])
{
    int a = 1;
    int b = 2;

    // [&a, b] 混合捕获:a引用捕获(可修改),b值捕获(只读)
    int sum = [&a, b]()->int{a=100; return a+b;}(); 
    cout << "sum: " << sum << endl; // 输出:sum: 102
    cout << "a: " << a << endl;     // 输出:a: 100(原变量被修改)

    return 0;
}
隐式捕获(批量捕获)

当外部变量较多时,可通过通配符批量捕获,分为值捕获([=])和引用捕获([&]):

// 隐式值捕获:捕获所有外部变量(只读)
auto f1 = [=]()->int{return a + b;}; 

// 隐式引用捕获:捕获所有外部变量(可读写)
auto f2 = [&]()->int{a=200; return a + b;}; 
混合隐式+显式捕获

支持“隐式+显式”组合,需注意:显式捕获的类型需与隐式捕获不同(避免冲突):

int a = 1, b = 2, c = 3;
// 隐式值捕获所有变量,显式引用捕获c(仅c可修改)
auto f3 = [=, &c](){c=10; return a+b+c;}; 
空捕获([]

不捕获任何外部变量,仅依赖形参列表接收参数,是最简洁的Lambda形式:

auto add = [](int x, int y){return x + y;};
cout << add(3,4) << endl; // 输出:7

mutable关键字的使用

默认情况下Lambda是const函数,值捕获的变量不可修改;mutable可取消常量性,允许修改值捕获的变量(仅修改内部拷贝,不影响原变量),使用时形参列表不可省略

#include <iostream>
using namespace std;

int main() {
    int a = 10;
    // mutable使用:形参列表()不可省略(即使无参数)
    auto f = [a]() mutable {a = 20; cout << "Lambda内a: " << a << endl;};
    f(); // 输出:Lambda内a: 20
    cout << "外部a: " << a << endl; // 输出:外部a: 10(仅修改内部拷贝)
    return 0;
}

返回类型的自动推断

Lambda可省略返回类型,编译器通过decltype自动推断,但需注意:函数体有多个return语句时,返回类型需一致,否则需显式声明:

// 省略返回类型:编译器推断为int
auto f1 = [](int x){return x * 2;}; 

// 错误示例:return类型不一致,需显式声明返回类型
// auto f2 = [](int x){if(x>0) return 1; else return 2.0;};

// 正确示例:显式声明返回类型为double
auto f2 = [](int x)->double{if(x>0) return 1.0; else return 2.0;};

形参列表的典型应用(STL算法库)

Lambda的形参列表核心场景是配合STL算法库使用,算法会自动将容器元素传递给Lambda的形参,示例如下:

#include <array>
#include <algorithm>
#include <iostream>
using namespace std;

int main(int argc, char const *argv[])
{
    // 定义STL数组容器(一维数组)
    array<int, 5> arr = {1,2,3,4,5};

    // count_if:统计容器中满足条件的元素数量
    // Lambda形参m由count_if自动传递(对应容器的每个int元素)
    int evenNum = count_if(arr.begin(), arr.end(), [](int m){return m%2==0;});

    cout << "偶数数目是: " << evenNum << endl; // 输出:偶数数目是: 2
    return 0;
}
count_if + Lambda 执行流程

拓展

Lambda的本质:闭包(Closure)

Lambda表达式在编译期会被编译器转换为一个匿名的闭包类,Lambda的调用等价于调用该类的operator()成员函数:

  • 值捕获:闭包类的成员变量存储外部变量的拷贝;
  • 引用捕获:闭包类的成员变量存储外部变量的引用;
  • mutable:对应闭包类的operator()成员函数被声明为非const。

Lambda与函数指针/函数对象的对比

类型 优势 劣势 适用场景
Lambda表达式 简洁、支持捕获外部变量、匿名 不可复用(除非赋值给函数对象) 一次性操作、STL算法配合
函数指针 复用性强、兼容C语言 不支持捕获外部变量、语法繁琐 跨C/C++场景、通用回调
类函数对象 可存储状态、复用性强 代码冗余(需定义类) 需复用且需维护状态的场景

Lambda的更多典型应用场景

STL链表排序(补充操作练习示例)

#include <list>
#include <algorithm>
#include <iostream>
using namespace std;

int main() {
    list<int> lst = {5,2,8,1,3};
    // Lambda作为sort的比较函数,实现链表升序排序
    lst.sort([](int a, int b){return a < b;});

    // 遍历输出排序后的链表
    for(auto num : lst) {
        cout << num << " "; // 输出:1 2 3 5 8
    }
    return 0;
}

回调函数(异步/事件处理场景)

#include <iostream>
#include <functional>
using namespace std;

// 模拟回调函数触发逻辑
void triggerCallback(function<void(int)> cb, int val) {
    cout << "触发回调,传入值:" << val << endl;
    cb(val);
}

int main() {
    int base = 10;
    // Lambda作为回调函数,捕获外部变量base
    triggerCallback([base](int val){
        cout << "回调处理结果:" << val + base << endl;
    }, 5); // 输出:触发回调,传入值:5 → 回调处理结果:15
    return 0;
}

统计链表中指定条件的元素数量(补充操作练习)

#include <list>
#include <algorithm>
#include <iostream>
using namespace std;

int main() {
    list<int> lst = {1,2,3,4,5,6,7,8};
    // 统计链表中大于4的元素数量
    int count = count_if(lst.begin(), lst.end(), [](int num){return num > 4;});
    cout << "大于4的元素数量:" << count << endl; // 输出:4
    return 0;
}

Lambda使用注意事项

  1. 引用捕获的生命周期:若Lambda的生命周期超过被捕获变量的生命周期,会导致悬垂引用(如捕获局部变量后,Lambda被传递到函数外部调用)。
  2. 空捕获的安全性:空捕获[]的Lambda不依赖外部变量,是最安全的Lambda形式,可放心传递。
  3. mutable的作用范围:仅修改值捕获的内部拷贝,不会影响外部原变量;引用捕获无需mutable即可修改原变量。
  4. 与函数指针的转换:无捕获的Lambda可通过+转换为函数指针,有捕获的Lambda不可转换:
    // 无捕获的Lambda转换为函数指针
    auto f = [](int x){return x*2;};
    int (*fp)(int) = +f; 
    cout << fp(3) << endl; // 输出:6
    
posted @ 2025-12-16 08:43  Jaklin  阅读(50)  评论(0)    收藏  举报