lambda表达式

C++11 引入的 Lambda 表达式是现代 C++ 中最强大的特性之一,它允许在代码中定义匿名函数,极大地提升了代码的灵活性和可读性。本文将深入探讨 Lambda 表达式的语法、捕获列表、使用场景以及编译器实现原理。

什么是 Lambda 表达式?

Lambda 表达式是一种匿名函数对象,可用于创建内联函数,无需显式定义函数名。其基本语法如下:

[capture list] (parameters) -> return_type { function_body }

  • capture list:捕获外部变量的方式(值捕获、引用捕获等)。
  • parameters:参数列表,与普通函数类似。
  • return_type:返回值类型,可省略(由编译器自动推导)。
  • function_body:函数体,包含具体逻辑。

示例:简单的加法 Lambda

auto plus = [] (int a, int b) { return a + b; };
int result = plus(3, 5); // 结果为 8

Lambda 表达式的四种常见形式

根据 C++ 标准,Lambda 表达式有四种典型写法:

  1. 完整形式

    [captures](params) -> return_type { body }
    
  2. 常量 Lambda(默认形式):

    [captures](params) { body }  // 返回值类型自动推导
    
  3. 省略返回值类型

    [captures](params) { return expression; }  // 返回值由 return 语句推导
    
  4. 无参数列表

    [captures] { body }  // 等价于 [captures]() { body }
    

捕获列表(Capture List)详解

捕获列表是 Lambda 表达式的核心特性,允许访问外部作用域的变量。常见的捕获方式有:

捕获形式说明
[]不捕获任何变量,Lambda 函数体内无法使用外部变量。
[=]值捕获所有外部变量,Lambda 内部使用副本,无法修改原始值。
[&]引用捕获所有外部变量,Lambda 内部可直接修改原始值。
[a, &b]值捕获 a,引用捕获 b
[=, &a, &b]除 a 和 b 引用捕获外,其他变量值捕获。
[&, a, b]除 a 和 b 值捕获外,其他变量引用捕获。
[this]在成员函数中捕获当前对象的 this 指针,可访问对象成员。

示例:捕获列表的使用

int x = 10, y = 20;

// 值捕获 x,无法修改
auto printX = [x] { std::cout << x << std::endl; };

// 引用捕获 y,可修改
auto incrementY = [&y] { y++; };

// 混合捕获
auto sum = [x, &y] { return x + y; };

Lambda 与 STL 算法的结合

Lambda 表达式最常见的应用场景是与 STL 算法结合,例如排序、查找、遍历等。

示例 1:自定义排序规则

struct Item {
    int a, b;
    Item(int aa, int bb) : a(aa), b(bb) {}
};

std::vector<Item> items = {{1, 19}, {10, 3}, {3, 7}};

// 按成员 a 升序排序
std::sort(items.begin(), items.end(),
    [] (const Item& i1, const Item& i2) {
        return i1.a < i2.a;
    });

示例 2:遍历并打印元素

// C++11 范围 for 循环 + Lambda
for (const auto& item : items) {
    [](const Item& i) {
        std::cout << "a=" << i.a << ", b=" << i.b << std::endl;
    }(item);
}

// 等价于使用 std::for_each
std::for_each(items.begin(), items.end(),
    [] (const Item& i) {
        std::cout << "a=" << i.a << ", b=" << i.b << std::endl;
    });

Lambda 表达式的进阶特性

1. mutable 关键字

默认情况下,值捕获的 Lambda 函数是常量函数,无法修改捕获的变量。使用 mutable 可解除此限制:

int x = 5;
auto modifyX = [x]() mutable {
    x++;  // 允许修改副本
    std::cout << x << std::endl;  // 输出 6
};
modifyX();
std::cout << x << std::endl;  // 外部 x 仍为 5
2. 泛型 Lambda(C++14)

使用 auto 作为参数类型,Lambda 可成为泛型函数:

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

int sum1 = add(1, 2);         // 整数相加
double sum2 = add(1.5, 2.5);  // 浮点数相加
3. 模板 Lambda(C++20)

使用模板参数列表(template)实现更灵活的泛型:

auto genericLambda = []<typename T>(const T& value) {
    std::cout << value << std::endl;
};

编译器如何实现 Lambda?

编译器会将 Lambda 表达式转换为一个匿名类(闭包类)的实例,并重载 operator()。例如:

Lambda 表达式

auto plus = [] (int a, int b) { return a + b; };

编译器生成的等价代码

class __lambda_plus {
public:
    int operator()(int a, int b) const {
        return a + b;
    }
};

__lambda_plus plus;
int result = plus(3, 5);

值捕获的 Lambda

int x = 10;
auto lambda = [x] { return x + 1; };

等价代码

class __lambda {
private:
    int x;  // 存储捕获的值

public:
    __lambda(int captured_x) : x(captured_x) {}

    int operator()() const {
        return x + 1;
    }
};

__lambda lambda(x);

Lambda 与函数指针的转换

无捕获列表的 Lambda 可隐式转换为函数指针,方便与 C 接口兼容:

using FuncPtr = int(*)(int, int);

// 无捕获的 Lambda
auto add = [] (int a, int b) { return a + b; };
FuncPtr ptr = add;  // 合法转换

int result = ptr(3, 5);  // 通过函数指针调用

总结

Lambda 表达式是 C++ 中强大而灵活的特性,通过简洁的语法实现匿名函数,尤其在 STL 算法、事件处理、异步编程中应用广泛。掌握 Lambda 的捕获列表、mutable 关键字及泛型特性,能让代码更简洁、更具表达力。

常见应用场景

  • 作为 STL 算法的谓词(如 sortfind_if)。
  • 简化回调函数的定义。
  • 在多线程编程中捕获上下文。
  • 实现轻量级的函数对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值