首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深入理解 C++ Lambda 表达式与函数式编程

深入理解 C++ Lambda 表达式与函数式编程

原创
作者头像
用户11690571
发布2025-06-21 19:05:39
发布2025-06-21 19:05:39
16700
代码可运行
举报
运行总次数:0
代码可运行

一、引言:从函数指针到 Lambda

在 C++ 98/03 时代,我们主要依靠函数指针和仿函数(Function Object)来传递行为:

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑boolisEven(int x) {
    return x % 2 == 0;
}

随着 C++11 的到来,引入了强大的 Lambda 表达式,让我们可以像 Python、JavaScript 一样优雅地定义 匿名函数、闭包函数和高阶函数

Lambda 表达式使得 C++ 函数式编程成为可能,成为现代 C++ 的重要特性。


二、Lambda 表达式语法详解

1. 语法格式

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑[capture](parameters) -> return_type {
    function_body;
}

2. 简单示例

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑auto add = [](int a, int b) -> int {
    return a + b;
};

std::cout << add(3, 4); // 输出 7

3. 省略返回类型(由 auto 推导)

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑auto multiply = [](int a, int b) {
    return a * b;
};

三、捕获列表(Capture List)

捕获外部变量的方式:

捕获方式

含义

[=]

值捕获所有外部变量

[&]

引用捕获所有外部变量

[x]

值捕获变量 x

[&x]

引用捕获变量 x

[=, &y]

默认值捕获,但 y 使用引用

[this]

捕获当前对象指针(用于类中)

示例:

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑int x = 10;
auto f = [x]() { return x + 1; };   // 捕获副本auto g = [&x]() { x += 1; };        // 修改 x

四、闭包对象与类型推导

Lambda 表达式其实是一个匿名类的 闭包对象(Closure Object),它重载了 operator(),因此可像函数一样调用。

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑auto lambda = [](int x) { return x * x; };

等价于:

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑struct__lambda {
    intoperator()(int x) const {
        return x * x;
    }
};

C++20 引入 decltype(auto) 和 concept,更好支持 Lambda 类型。


五、Lambda 与 STL 算法结合

Lambda 与 STL 算法完美契合,提升代码表达力。

1. std::for_each

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑std::vector<int> v = {1, 2, 3};
std::for_each(v.begin(), v.end(), [](int x) {
    std::cout << x << " ";
});

2. std::sort

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑std::vector<int> v = {3, 1, 4};
std::sort(v.begin(), v.end(), [](int a, int b) {
    return a > b;
});

3. std::count_if

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑int count = std::count_if(v.begin(), v.end(), [](int x) {
    return x % 2 == 0;
});

六、可变 Lambda(mutable)

默认情况下,捕获变量为只读,若想修改值副本,需加 mutable。

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑int x = 10;
auto f = [x]() mutable {
    x += 1;           // 修改副本,不影响外部 xreturn x;
};

f(); // 返回 11
std::cout << x; // 仍为 10

七、Lambda 表达式作为返回值

Lambda 不能直接推导类型,需要使用 std::function 或 auto。

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑#include<functional>std::function<int(int)> makeAdder(int base) {
    return [base](int x) {
        return base + x;
    };
}

auto add5 = makeAdder(5);
std::cout << add5(3); // 输出 8

八、高阶函数:函数作为参数或返回值

示例:执行器模式

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑voidexecute(const std::function<void()> &func) {
    func();
}

execute([] {
    std::cout << "Hello from Lambda!" << std::endl;
});

九、Lambda 与类成员函数

在类中使用 Lambda:

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑classPrinter {
public:
    voidrun() {
        int count = 3;
        auto printer = [this, count]() {
            for (int i = 0; i < count; ++i)
                std::cout << "Value: " << i + value << std::endl;
        };
        printer();
    }
private:
    int value = 10;
};

捕获 [this] 让 Lambda 可访问成员变量。


十、Lambda 实现递归(Y Combinator 技巧)

Lambda 本身无法直接递归调用自己,可以通过 std::function 或 Y Combinator 技巧实现。

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑std::function<int(int)> fib = [&](int n) {
    return (n <= 1) ? n : fib(n - 1) + fib(n - 2);
};

或者更现代写法(C++14):

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑auto fib = [](auto self, int n) -> int {
    return (n <= 1) ? n : self(self, n - 1) + self(self, n - 2);
};

std::cout << fib(fib, 6); // 输出 8

十一、Lambda 与并发:线程任务函数

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑#include<thread>intmain() {
    int x = 42;
    std::thread t([x]() {
        std::cout << "Thread value: " << x << std::endl;
    });
    t.join();
}

在多线程任务中,Lambda 极其便捷地绑定任务上下文。


十二、Lambda 性能与底层实现

1. Lambda 实际为类对象(闭包),编译期生成匿名类。

2. 若无捕获,Lambda 转换为函数指针,零开销。

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑auto f = [] (int x) { return x * x; }; // 无捕获int (*fp)(int) = f;  // OK

3. 有捕获时为对象,不能转函数指针。


十三、C++20 捕获 by move(移动捕获)

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑std::unique_ptr<int> up = std::make_unique<int>(10);
auto lambda = [ptr = std::move(up)]() {
    std::cout << *ptr << std::endl;
};

此技术称为 初始化捕获,C++14 起支持,C++20 支持 =std::move(...)。


十四、图解:Lambda 捕获与类型推导

代码语言:javascript
代码运行次数:0
运行
复制
text复制编辑int a = 10, b = 20;
auto f = [a, &b]() mutable {
    a++;  // 修改副本,不影响外部
    b++;  // 修改引用,影响外部
};

图示:

代码语言:javascript
代码运行次数:0
运行
复制
markdown复制编辑Lambda 对象:
--------------------------| a(副本) = 10           || b(引用) → 外部 b       |
--------------------------

十五、实战案例:高阶筛选器生成器

代码语言:javascript
代码运行次数:0
运行
复制
cpp复制编辑auto make_filter = [](int threshold) {
    return [threshold](int x) {
        return x > threshold;
    };
};

auto filter10 = make_filter(10);
std::cout << filter10(15); // true
std::cout << filter10(5);  // false

该 Lambda 工厂函数生成高阶条件过滤器。


十六、总结与建议

特性

支持版本

描述

Lambda 表达式

C++11

匿名函数支持

mutable Lambda

C++11

修改值捕获副本

初始化捕获

C++14

[val = expr]

泛型 Lambda

C++14

[ ](auto x){}

捕获 by move

C++20

[ptr = std::move(...) ]

✅ 学习建议:

  • 多使用 Lambda 与 STL 算法结合提升代码简洁性
  • 熟悉闭包、引用捕获、值捕获的语义差异
  • 掌握递归 Lambda、线程任务、函数对象替代等实战技巧
  • 学会 std::function 包装函数对象并作为参数传递

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、引言:从函数指针到 Lambda
  • 二、Lambda 表达式语法详解
    • 1. 语法格式
    • 2. 简单示例
    • 3. 省略返回类型(由 auto 推导)
  • 三、捕获列表(Capture List)
    • 示例:
  • 四、闭包对象与类型推导
  • 五、Lambda 与 STL 算法结合
    • 1. std::for_each
    • 2. std::sort
    • 3. std::count_if
  • 六、可变 Lambda(mutable)
  • 七、Lambda 表达式作为返回值
  • 八、高阶函数:函数作为参数或返回值
    • 示例:执行器模式
  • 九、Lambda 与类成员函数
  • 十、Lambda 实现递归(Y Combinator 技巧)
  • 十一、Lambda 与并发:线程任务函数
  • 十二、Lambda 性能与底层实现
    • 1. Lambda 实际为类对象(闭包),编译期生成匿名类。
    • 2. 若无捕获,Lambda 转换为函数指针,零开销。
    • 3. 有捕获时为对象,不能转函数指针。
  • 十三、C++20 捕获 by move(移动捕获)
  • 十四、图解:Lambda 捕获与类型推导
  • 十五、实战案例:高阶筛选器生成器
  • 十六、总结与建议
    • ✅ 学习建议:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档