C++11中Lambda表达式的介绍和使用

一、概述

C++11 引入的 lambda 表达式是现代 C++ 中非常重要的特性,它简化了匿名函数的定义和使用,尤其在 STL 算法、回调函数等场景中广泛应用。

二、Lambda 表达式的基本语法

[capture-list](parameters) mutable -> return-type { 
    // 函数体 
}

1. 捕获列表(Capture List)

  • 用于捕获外部变量,控制 lambda 如何访问其作用域外的变量。
  • 捕获方式:
    • []:不捕获任何变量。
    • [=]:隐式值捕获(所有外部变量以值传递)。
    • [&]:隐式引用捕获(所有外部变量以引用传递)。
    • [var]:显式值捕获单个变量。
    • [&var]:显式引用捕获单个变量。
    • 混合捕获:[=, &var] 或 [&, var]。

2. 参数列表(Parameters)

  • 类似普通函数的参数列表。
  • 无参数时可省略,如 []{}。

3. mutable 关键字

  • 默认情况下,lambda 的 operator() 是 const 的,不能修改值捕获的变量。
  • 使用 mutable 后,可以修改值捕获的变量(但修改不影响外部变量)。

4. 返回类型(Return Type)

  • 可省略,编译器自动推导。
  • 复杂逻辑需显式指定返回类型,如 -> int。

三、Lambda 的实现原理

在 C++11 中,Lambda 表达式的底层实现原理本质上是 “匿名仿函数(函数对象)”,即编译器会将 Lambda 转换为一个 匿名类,然后用该类的对象来调用 operator()。这个类可能会存储捕获的变量,并根据不同的捕获方式(值捕获、引用捕获等)来决定它的成员变量和 operator() 方法的行为。

Lambda 转换为匿名类:
示例 Lambda 表达式

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

等价转换的 C++ 代码 编译器会生成一个类似如下的匿名类:

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

然后 lambda 变量实际上是该类的一个实例:

LambdaClass lambda;
int result = lambda(10); // 等价于 lambda.operator()(10)

四、Lambda 捕获变量的实现

如果 Lambda 需要捕获变量,编译器会在生成的匿名类中存储这些变量,并在 operator() 中使用它们。

1.值捕获([a])

int a = 10;
auto lambda = [a]() { return a + 1; };

编译器生成的类:

class LambdaClass {
    int a;  // 复制 a 的值

    LambdaClass(int a_) : a(a_) {}  // 通过构造函数初始化

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

实际调用:

LambdaClass lambda(10);
int result = lambda();  // 11

这里 a 被复制到 LambdaClass 中,因此即使 a 的原始变量作用域结束,该 Lambda 仍然可以访问 a 的值。

2.引用捕获([&a])

int a = 10;
auto lambda = [&a]() { return a + 1; };

编译器生成的类:

class LambdaClass {
    int& a;  // 引用 a

    LambdaClass(int& a_) : a(a_) {}  // 通过引用初始化

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

实际调用:

a = 20;
LambdaClass lambda(a);
int result = lambda();  // 21

因为 a 是按引用捕获的,lambda() 调用时会访问 a 的最新值。

3.隐式捕获 [=] 和 [&]
如果使用 [=],所有变量都会按值捕获;如果使用 [&],所有变量都会按引用捕获:

int x = 5, y = 10;
auto lambda1 = [=]() { return x + y; };  // 值捕获
auto lambda2 = [&]() { return x + y; };  // 引用捕获

其底层实现类似于:

class LambdaClass1 {
    int x, y;
    LambdaClass1(int x_, int y_) : x(x_), y(y_) {}
    int operator()() const { return x + y; }
};

class LambdaClass2 {
    int &x, &y;
    LambdaClass2(int& x_, int& y_) : x(x_), y(y_) {}
    int operator()() const { return x + y; }
};

4.Lambda 的 mutable 关键字
默认情况下,Lambda 的 operator() 是 const,即不能修改值捕获的变量:

int a = 10;
auto lambda = [a]() mutable { a += 5; return a; };

编译器生成的类:

class LambdaClass {
    int a;
    LambdaClass(int a_) : a(a_) {}

    int operator()() { // 不是 const 版本
        a += 5;
        return a;
    }
};

注意:mutable 允许修改值捕获的变量,但不会影响外部变量。

5.Lambda 的 std::function 适配
Lambda 也可以转换为 std::function,但会涉及类型擦除:

#include <functional>
#include <iostream>

int main() {
    int a = 10;
    std::function<int()> f = [a]() { return a + 1; }; // Lambda 转换为 std::function
    std::cout << f() << std::endl; // 输出 11
}

此时 std::function 内部会存储一个 动态分配的 Lambda 对象,增加了一定的开销。

6.Lambda 是什么类型?
Lambda 不能直接获取类型,但可以使用 decltype 推导:

auto lambda = [](int x) { return x + 1; };
using LambdaType = decltype(lambda);

或者 typeid:

#include <typeinfo>
#include <iostream>

int main() {
    auto lambda = [](int x) { return x + 1; };
    std::cout << typeid(lambda).name() << std::endl;
}

每个 Lambda 都有一个 独特的类型,但都可以自动转换为 函数指针(如果没有捕获变量):

using FuncPtr = int(*)(int);
FuncPtr func = [](int x) { return x + 1; };

五、总结

  • Lambda 本质上是 匿名类,其 operator() 方法定义 Lambda 逻辑。
  • 如果有捕获,编译器会将捕获的变量存储为类的成员变量。
  • mutable 允许修改值捕获的变量,但不会影响外部变量。
  • 无捕获的 Lambda 可以转换为函数指针,有捕获的 Lambda 需要使用 std::function。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值