菜单

Administrator
发布于 2026-05-22 / 1 阅读
0
0

Java 开发者学 C++(六):Lambda 表达式与函数式编程

导语:为什么 Lambda 是 C++11 最重要的特性之一

如果你问一个资深 C++ 程序员「C++11 哪个特性改变了你的编码方式」,Lambda 表达式一定排在前三。不是因为 Lambda 本身有多新奇——Java 早在 2014 年就引入了——而是因为 Lambda 彻底激活了 STL 算法的威力,让 C++ 从"命令式的循环拼接"进化到了"声明式的数据管道"。

在 C++11 之前,想对一组数据做筛选、变换、聚合,你需要写一堆手写循环、临时变量、辅助函数对象(functor)。一个简单的"从列表中找到所有偶数并平方"要七八行代码,而且意图被淹没在迭代器操作里。Lambda 改变了这一切:

// C++03:冗长的函数对象
struct IsEven {
    bool operator()(int x) const { return x % 2 == 0; }
};
std::vector<int> evens;
std::remove_copy_if(v.begin(), v.end(), std::back_inserter(evens),
                    std::not1(IsEven()));  // 简直反人类

// C++11:Lambda 一句搞定
std::vector<int> evens;
std::copy_if(v.begin(), v.end(), std::back_inserter(evens),
             [](int x) { return x % 2 == 0; });

更关键的是,C++ 的 Lambda 远比 Java 强大。Java 的 Lambda 本质上是函数式接口的语法糖,捕获变量必须是 effectively final,没有泛型参数,没有移动语义。C++ 的 Lambda 可以做值捕获、引用捕获、移动捕获、模板参数推导,甚至可以定义自己的成员变量。它是真正的"匿名函数对象",而不是接口的语法糖。

本文将带你从 Java 的 Lambda 出发,深入 C++ Lambda 的每一个角落。读完你会发现:在函数式编程这件事上,C++ 的水比 Java 深得多。


Java Lambda 快速回顾——你已有的知识

在深入 C++ 之前,我们先快速回顾一下你已经熟悉的 Java Lambda。这能帮我们建立对照坐标系。

函数式接口:Lambda 的"类型"

Java 中,Lambda 表达式的类型是一个函数式接口(只有一个抽象方法的接口):

// Java Lambda 必须赋值给函数式接口
Function<Integer, Integer> square = x -> x * x;
Predicate<Integer> isEven = x -> x % 2 == 0;
Consumer<String> printer = s -> System.out.println(s);
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;

你不能写 var f = x -> x * 2; 然后让编译器自动推导——Java 必须从一个函数式接口的上下文推断类型。这是 Java Lambda 和 C++ Lambda 最根本的区别之一。

Stream API:Lambda 的主战场

Java 中 Lambda 最常见的用法是配合 Stream API:

List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);

// 筛选 + 变换 + 收集
List<Integer> result = numbers.stream()
    .filter(x -> x % 2 == 0)       // 筛选偶数
    .map(x -> x * x)                // 平方
    .sorted()                        // 排序
    .collect(Collectors.toList());   // 收集

Java Lambda 的关键限制

作为 Java 开发者,你可能已经习惯了这些约束,但它们是理解 C++ Lambda 优势的绝佳切入点:

  1. 捕获变量必须是 effectively final:Lambda 能读取外部局部变量,但不能修改,变量也不能被重新赋值
  2. 不能泛型化(T x) -> x 是不合法的,没有"泛型 Lambda"
  3. 没有移动语义:Java 是 GC 语言,不需要也不支持移动捕获
  4. 有且仅有一个抽象方法的接口才能用 Lambda:本质上是匿名内部类的语法糖

带着这些认知,我们来看 C++ 是如何突破每一个限制的。


C++ Lambda 基础语法对比

语法骨架

C++ Lambda 的完整语法形式是:

[捕获列表](参数列表) mutable -> 返回类型 { 函数体 }

四个部分中,只有捕获列表函数体是必须的。最简形式:

[]{}  // 合法!一个什么都不做的 Lambda

来个直观的 Java ↔ C++ 语法对比:

// Java
x -> x * 2
(a, b) -> a + b
() -> System.out.println("Hello")
(String s) -> s.toUpperCase()
// C++
[](int x) { return x * 2; }
[](int a, int b) { return a + b; }
[]() { std::cout << "Hello"; }
[](const std::string& s) { return ...; /* C++ 没有 toUpperCase */ }

返回类型推导

C++ 编译器会自动推导 Lambda 的返回类型,所以你通常不需要写 -> 返回类型

// 编译器推导返回 int
auto add = [](int a, int b) { return a + b; };

// 编译器推导返回 double
auto half = [](int x) { return x / 2.0; };

// 多条 return 语句时,所有返回类型必须一致
auto weird = [](int x) {
    if (x > 0) return 1;
    else return 2;   // OK,都是 int
};

// ❌ 不一致会导致编译错误
// auto bad = [](int x) {
//     if (x > 0) return 1;
//     else return 1.0;  // int vs double,推导失败
// };

显式指定返回类型

当有多个返回语句且需要类型转换,或者你想要明确表达意图时,可以显式指定:

// 显式指定返回 double,将所有返回统一转换
auto convert = [](int x) -> double {
    if (x > 0) return 1;
    else return 1.5;   // OK,都转成 double
};

auto:C++ Lambda 的类型

// ✅ 这是 C++ Lambda 的正确使用方式
auto square = [](int x) { return x * x; };

C++ 中每个 Lambda 表达式都有一个独一无二的编译器生成类型。你不能写出它的类型名,只能用 auto 来承接。这就是"闭包类型"(closure type)——每个 Lambda 都是它自己闭包类型的一个实例。

// 每个 Lambda 都有独特的类型
auto f1 = [](int x) { return x * 2; };
auto f2 = [](int x) { return x * 2; };
// f1 和 f2 的类型不同!即使函数体完全一样
// f1 = f2; // ❌ 编译错误:类型不匹配

这与 Java 形成鲜明对比——Java 中两个相同的 Lambda 可以赋值给同一个函数式接口变量。


捕获列表详解——这才是 C++ Lambda 的灵魂

如果你只从本文记住一件事,请记住它:捕获列表是 C++ Lambda 和 Java Lambda 最大的分水岭

Java Lambda 只能读取外部的 effectively final 变量,仅此而已。C++ Lambda 的捕获列表提供了五种截然不同的捕获方式,每一种都对应着不同的所有权语义和性能特征。

捕获的基本语法

捕获列表写在方括号 [] 里:

int factor = 10;

// 按值捕获 factor
auto times = [factor](int x) { return x * factor; };

std::cout << times(5);  // 50

值捕获 [=]:拷贝一份,互不影响

默认情况下,值捕获的变量在 Lambda 内部是 const——就像 Java 的 effectively final

int counter = 0;

auto f = [counter]() {
    // counter++;  // ❌ 编译错误:counter 是只读的
    return counter; // ✅ 可以读
};

counter = 100;        // 修改外部变量
std::cout << f();     // 输出 0——Lambda 内部是独立的拷贝

要修改值捕获的副本,需要加 mutable 关键字:

int counter = 0;

auto f = [counter]() mutable {
    counter++;           // ✅ mutable 允许修改副本
    return counter;
};

std::cout << f();  // 1
std::cout << f();  // 2  —— 副本在 Lambda 内部持续存在
std::cout << counter;  // 0  —— 外部原变量不变

关键理解mutable 的 Lambda 像一个带着内部状态的小对象。每次调用都会更新它自己的私有副本。

引用捕获 [&]:共享同一份数据

引用捕获让 Lambda 直接操作外部变量。这就像 Java 中传入了一个可变容器(如数组或 AtomicInteger):

int sum = 0;
std::vector<int> v = {1, 2, 3, 4, 5};

// 引用捕获 sum——Lambda 直接修改外部变量
std::for_each(v.begin(), v.end(), [&sum](int x) { sum += x; });

std::cout << sum;  // 15

⚠️ 悬垂引用是引用捕获最大的陷阱!

std::function<int()> create_dangling() {
    int local = 42;
    return [&local]() { return local; };  // ❌ 危险!
    // local 在函数返回后被销毁,Lambda 持有悬垂引用
}

auto f = create_dangling();
// std::cout << f();  // 未定义行为!local 已不存在

Java 开发者必读:Java 中你不必担心这个问题,因为 JVM 的 GC 会保证对象在引用存在期间不被回收。C++ 没有 GC——你需要自己保证被引用捕获的变量在 Lambda 被调用时还活着。

隐式捕获 [=] 和 [&]:偷懒的艺术

你可以不逐个列出要捕获的变量,而是用 [=][&] 让编译器自动捕获 Lambda 体内用到的所有外部变量:

int a = 1, b = 2, c = 3;

// [=]:以值方式捕获所有用到的变量
auto f1 = [=]() { return a + b + c; };

// [&]:以引用方式捕获所有用到的变量
auto f2 = [&]() { a++; b++; c++; };

混合捕获:可以指定默认捕获方式,同时给特定变量指定不同的捕获方式:

int a = 1, b = 2, c = 3;

// 默认值捕获,但 b 用引用捕获
auto f = [=, &b]() {
    // a: 值捕获(只读)
    // b: 引用捕获(可修改)
    // c: 值捕获(只读)
    b = a + c;  // ✅
    return b;
};

// 默认引用捕获,但 c 用值捕获
auto g = [&, c]() {
    a++;       // ✅ 引用捕获
    // c++;    // ❌ c 是值捕获,只读
    return a + c;
};

表达式捕获 / init-capture(C++14):捕获任意表达式的结果

这是 C++14 最让人兴奋的 Lambda 特性!你可以在捕获列表中执行任意表达式,并将结果作为 Lambda 的成员变量:

// 捕获表达式的结果
auto f = [x = 42, y = std::string("hello")]() {
    return x;
};

// 实际场景:捕获 unique_ptr(不可拷贝,只能移动)
auto ptr = std::make_unique<int>(42);

// ❌ [ptr] 不行——unique_ptr 不能被拷贝
// ❌ [&ptr] 虽然可行但危险(ptr 出作用域后悬垂)
// ✅ [p = std::move(ptr)]:移动所有权到 Lambda 内部
auto f2 = [p = std::move(ptr)]() {
    return *p;
};
// ptr 现在是 nullptr,所有权已移至 Lambda 内部的 p

表达式捕获让你可以在 Lambda 内部拥有独立的资源,而不需要依赖外部变量的生命周期。这就像 Java 中把对象所有权完全交给 Lambda——但在 C++ 中,这是编译期保证的零开销转移。

移动捕获详解(C++14)

移动捕获是表达式捕获最重要的应用场景。在 C++11 中,你无法将 unique_ptrthreadfstream 等只移动类型捕获到 Lambda 中——唯一的选择是引用捕获,但引用捕获要求原对象在 Lambda 调用时依然存活。

#include <memory>
#include <thread>
#include <fstream>
#include <vector>

// 场景1:将 unique_ptr 移动到 Lambda
auto ptr = std::make_unique<std::vector<int>>(std::vector<int>{1, 2, 3});

auto process = [vec = std::move(ptr)]() {
    // vec 是 std::unique_ptr<std::vector<int>>
    for (int x : *vec) {
        std::cout << x << " ";
    }
};

process();  // ptr 已失效,数据归 Lambda 所有

// 场景2:将线程移动到 Lambda
std::thread t([]{ std::cout << "worker\n"; });
auto holder = [t = std::move(t)]() mutable {
    if (t.joinable()) {
        t.join();
    }
};

// 场景3:将大对象移动到 Lambda,避免拷贝
std::vector<int> huge_data(1000000);  // 一百万元素的 vector
auto consumer = [data = std::move(huge_data)]() {
    return data.size();
};
// huge_data 现在是空的,数据已移入 Lambda

Java 类比:Java 中你把对象引用传给 Lambda 就是"移动"了(因为没有所有权概念)。但在 C++ 中,移动语义是显式的、编译期保证的零开销操作——移动一个 vector 只拷贝三个指针,而不是一百万个元素。

this 捕获与 *this 捕获(C++17)

在成员函数中定义 Lambda 时,你可能需要访问成员变量:

class Widget {
    int value = 42;

    auto get_lambda_cpp11() {
        // [this]:按引用捕获当前对象(捕获 this 指针)
        return [this]() { return value; };
        // 等价于 return [this]() { return this->value; };
    }

    auto get_lambda_cpp17() {
        // [*this]:按值捕获当前对象(拷贝整个对象!)
        return [*this]() { return value; };
        // Lambda 内部有 Widget 的完整拷贝
    }
};

[this] vs [*this] 的关键区别

class Widget {
    int value = 42;
public:
    void setValue(int v) { value = v; }

    auto lambda_this() {
        return [this]() { return value; };
        // ⚠️ 返回的 Lambda 持有 this 指针
        // 如果原对象被析构,Lambda 中的 this 悬垂!
    }

    auto lambda_star_this() {
        return [*this]() { return value; };
        // ✅ 返回的 Lambda 持有 Widget 的独立拷贝
        // 即使原对象析构,Lambda 依然安全
    }
};

Widget w;
auto f1 = w.lambda_this();
auto f2 = w.lambda_star_this();

w.setValue(100);

std::cout << f1();  // 100 —— 引用原对象,反映修改
std::cout << f2();  // 42  —— 持有拷贝,不受修改影响

Java 对照:Java 内部类持有外部类的隐式引用(OuterClass.this),行为类似 [this] 捕获。C++ 的 [*this] 给你多一个选择:完全独立于原对象的副本。这在异步编程中非常有用——你不需要担心原对象的生命周期。

捕获列表速查表

捕获语法 含义 适用 C++ 版本 常见场景
[x] 按值捕获 x C++11 需要外部值的副本
[&x] 按引用捕获 x C++11 需要修改外部变量
[=] 按值捕获所有使用的变量 C++11 快速 lambda,不修改外部
[&] 按引用捕获所有使用的变量 C++11 需要大量修改外部变量
[this] 按引用捕获当前对象 C++11 成员函数内访问成员
[*this] 按值捕获当前对象 C++17 异步场景,独立于原对象
[x = expr] 捕获表达式结果 C++14 移动捕获、初始化新变量
[=, &x] 默认值捕获,x 引用捕获 C++11 混合捕获
[&, x] 默认引用捕获,x 值捕获 C++11 混合捕获

泛型 Lambda(C++14):Java 没有的利器

这是另一个 Java Lambda 做不到而 C++ 轻松驾驭的特性。

auto 参数

C++14 允许 Lambda 参数使用 auto,让编译器为每次调用推导类型:

// 一个"万能加法" Lambda
auto add = [](auto a, auto b) { return a + b; };

std::cout << add(1, 2);           // 3       —— int
std::cout << add(1.5, 2.5);       // 4.0     —— double
std::cout << add(std::string("Hello "), std::string("World"));  // "Hello World"

// 对于不支持 operator+ 的类型,会在调用时(而非定义时)报错
// add(std::vector<int>{}, std::vector<int>{});  // 编译错误:vector 没有 operator+

原理:泛型 Lambda 的 auto 参数让编译器生成一个模板化的 operator()。上面的 add 大致等价于:

struct __anonymous {
    template<typename T, typename U>
    auto operator()(T a, U b) const { return a + b; }
};

泛型 Lambda 的实际应用

// 1. 通用的"打印到流" Lambda
auto printer = [](const auto& value) {
    std::cout << value << " ";
};

std::vector<int> vi = {1, 2, 3};
std::vector<std::string> vs = {"a", "b", "c"};

std::for_each(vi.begin(), vi.end(), printer);  // 1 2 3
std::for_each(vs.begin(), vs.end(), printer);  // a b c

// 2. 通用的 pair/tuple 解构(C++17 结构化绑定更优雅)
auto print_pair = [](const auto& p) {
    std::cout << "(" << p.first << ", " << p.second << ")";
};

// 3. 结合多个容器类型
auto multiply_by = [](auto factor) {
    return [factor](auto x) { return x * factor; };
    // 返回的 Lambda 接受任意数值类型
};

auto times2 = multiply_by(2);
std::cout << times2(3.14);   // 6.28  —— double
std::cout << times2(10);     // 20    —— int

Java 对比:Java 中你能写的最接近的是 Function<Object, Object> 然后强制转型——类型安全荡然无存。C++ 的泛型 Lambda 是编译期多态,零运行时开销,类型安全完全保留。


std::function:通用的函数包装器

Lambda 虽然强大,但每个 Lambda 都有自己独特的类型。如果你想存储 Lambda、把它作为函数参数、或者放进容器里,就需要 std::function

基本用法

#include <functional>
#include <iostream>
#include <vector>

// std::function 可以包装任何可调用对象
std::function<int(int, int)> op;

op = [](int a, int b) { return a + b; };
std::cout << op(3, 4);  // 7

op = [](int a, int b) { return a * b; };
std::cout << op(3, 4);  // 12

// 也可以包装函数指针和函数对象
int subtract(int a, int b) { return a - b; }
op = subtract;
std::cout << op(10, 3);  // 7

op = std::multiplies<int>{};  // 标准库函数对象
std::cout << op(5, 6);  // 30

std::function 作为参数

// 接受任意匹配签名的可调用对象
void process(int n, const std::function<void(int)>& callback) {
    for (int i = 0; i < n; ++i) {
        callback(i);
    }
}

process(5, [](int x) { std::cout << x << " "; });
process(3, [count = 0](int x) mutable { std::cout << ++count; });

存储 Lambda 到容器

// 不同 Lambda 可以放进同一个 vector(只要签名匹配)
std::vector<std::function<int(int, int)>> operations;

operations.push_back([](int a, int b) { return a + b; });
operations.push_back([](int a, int b) { return a - b; });
operations.push_back([](int a, int b) { return std::max(a, b); });
operations.push_back(std::multiplies<int>{});

for (auto& op : operations) {
    std::cout << op(10, 3) << " ";  // 13 7 10 30
}

std::function 的开销

重要警示std::function 使用类型擦除,有运行时开销(通常是一次虚函数调用 + 可能的内存分配)。对于性能敏感的代码,优先用模板或 auto

// ❌ 不必要的 std::function 开销
template<typename F>
void process_slow(const std::function<void(int)>& f) {
    for (int i = 0; i < 1000000; ++i) f(i);
}

// ✅ 模板参数:零开销,编译器内联
template<typename F>
void process_fast(F&& f) {
    for (int i = 0; i < 1000000; ++i) f(i);
}

// ✅ auto:也是零开销
auto lambda = [](int x) { return x * 2; };
// lambda 保留了真实类型,无 std::function 开销

Java 对照std::function 类似于 Java 中的函数式接口(Function<T,R>Consumer<T> 等),但 std::function 是值语义的——赋值是拷贝,而 Java 的接口是引用语义。


std::bind 与占位符:提前绑定参数

std::bind 允许你固定函数的某些参数,生成一个新的可调用对象。虽然 Lambda 在很多场景下更灵活,但 std::bind 在特定场景下依然实用。

基本用法

#include <functional>
#include <iostream>

// 定义一个普通函数
int multiply(int a, int b) {
    return a * b;
}

// std::bind 固定第一个参数
using namespace std::placeholders;  // _1, _2, ...

auto times2 = std::bind(multiply, 2, std::placeholders::_1);
std::cout << times2(5);   // 10 —— multiply(2, 5)
std::cout << times2(10);  // 20 —— multiply(2, 10)

// 固定第二个参数
auto double_it = std::bind(multiply, std::placeholders::_1, 2);
std::cout << double_it(5);  // 10

// 改变参数顺序
auto flipped = std::bind(multiply, std::placeholders::_2, std::placeholders::_1);
std::cout << flipped(2, 5);  // 10 —— multiply(5, 2)

绑定成员函数

class Calculator {
    int base_;
public:
    Calculator(int base) : base_(base) {}
    int add(int x) const { return base_ + x; }
};

Calculator calc(10);

// 绑定成员函数——第一个参数是对象指针/引用
auto add_to_calc = std::bind(&Calculator::add, &calc, std::placeholders::_1);
std::cout << add_to_calc(5);   // 15
std::cout << add_to_calc(20);  // 30

// 也可以用 std::mem_fn 专门处理成员函数
auto add_fn = std::mem_fn(&Calculator::add);
std::cout << add_fn(calc, 5);  // 15

std::bind vs Lambda

大多数场景下,Lambda 比 std::bind 更清晰:

// std::bind 风格——难以阅读
auto f1 = std::bind(&std::vector<int>::push_back,
                    std::ref(vec), std::placeholders::_1);

// Lambda 风格——意图明确
auto f2 = [&vec](int x) { vec.push_back(x); };

何时用 std::bind

  • 需要传递可调用对象给仅接受 std::function 的旧接口
  • 复杂的参数重排和绑定场景(但 Lambda 嵌套通常也能做到)
  • std::ref / std::cref 配合使用时

Lambda 的典型应用场景

场景一:STL 算法——Lambda 的天然主场

这是 Lambda 最常见的应用场景。STL 有 100+ 个算法,几乎每个都接受一个可调用对象作为参数。

#include <algorithm>
#include <vector>
#include <numeric>
#include <iostream>

std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 筛选(filter)
std::vector<int> evens;
std::copy_if(data.begin(), data.end(), std::back_inserter(evens),
             [](int x) { return x % 2 == 0; });

// 变换(map)
std::vector<int> squared(data.size());
std::transform(data.begin(), data.end(), squared.begin(),
               [](int x) { return x * x; });

// 排序——自定义比较器
std::sort(data.begin(), data.end(),
          [](int a, int b) { return std::abs(a - 5) < std::abs(b - 5); });
// 按离5的距离排序:{5, 4, 6, 3, 7, 2, 8, 1, 9, 10}

// 查找第一个满足条件的元素
auto it = std::find_if(data.begin(), data.end(),
                       [](int x) { return x > 7 && x % 2 == 0; });

// 统计满足条件的元素个数
int count = std::count_if(data.begin(), data.end(),
                          [](int x) { return x % 3 == 0; });

// 批量操作:找到所有能被3整除的数,乘以2,求和
int sum = std::accumulate(data.begin(), data.end(), 0,
    [](int acc, int x) {
        return x % 3 == 0 ? acc + x * 2 : acc;
    });

场景二:回调函数

#include <functional>
#include <iostream>
#include <string>

// 通用的事件处理器
class Button {
    std::function<void()> onClick_;
public:
    void setOnClick(std::function<void()> handler) {
        onClick_ = std::move(handler);
    }
    void click() { if (onClick_) onClick_(); }
};

int main() {
    Button btn;
    int clickCount = 0;

    // 引用捕获计数器
    btn.setOnClick([&clickCount]() {
        clickCount++;
        std::cout << "Clicked " << clickCount << " times\n";
    });

    btn.click();  // Clicked 1 times
    btn.click();  // Clicked 2 times

    // 值捕获 + mutable:Lambda 自带状态
    auto handler = [count = 0]() mutable {
        std::cout << "Call #" << ++count << "\n";
    };
    btn.setOnClick(handler);
    btn.click();  // Call #1
    btn.click();  // Call #2
}

场景三:RAII 辅助——Scope Guard

这是 C++ Lambda 的一个优雅应用:利用 RAII 在作用域退出时自动执行清理代码。

#include <functional>
#include <iostream>

// 简单的 scope guard 实现
class ScopeGuard {
    std::function<void()> onExit_;
public:
    explicit ScopeGuard(std::function<void()> f) : onExit_(std::move(f)) {}
    ~ScopeGuard() { onExit_(); }
    ScopeGuard(const ScopeGuard&) = delete;
    ScopeGuard& operator=(const ScopeGuard&) = delete;
};

void process_file(const std::string& path) {
    std::cout << "Opening file: " << path << "\n";
    // 模拟资源获取

    // ✅ 无论函数如何退出,这个 Lambda 都会执行
    ScopeGuard guard([&path]() {
        std::cout << "Closing file: " << path << "\n";
    });

    // 复杂的处理逻辑...
    if (path.empty()) {
        return;  // 提前返回——guard 仍然执行
    }

    // 可能抛异常的代码
    std::cout << "Processing...\n";

}  // guard 析构,自动执行清理

int main() {
    process_file("data.txt");
    // 输出:
    // Opening file: data.txt
    // Processing...
    // Closing file: data.txt

    process_file("");  // 提前返回
    // 输出:
    // Opening file:
    // Closing file:      ← 依然执行了清理!
}

Java 对照:这相当于 Java 的 try-with-resources 或 finally 块的 C++ 版本。但 C++ 的 scope guard 更灵活——你可以用 Lambda 定义任意清理逻辑,不局限于 Closeable 接口。

场景四:异步任务

#include <future>
#include <iostream>
#include <thread>
#include <vector>

// std::async + Lambda
auto future = std::async(std::launch::async, []() {
    // 在另一个线程中执行的耗时计算
    int result = 0;
    for (int i = 0; i < 1000000; ++i) {
        result += i;
    }
    return result;
});

// 主线程继续做其他事...
std::cout << "Doing other work...\n";

// 获取结果(会阻塞直到计算完成)
int final_result = future.get();
std::cout << "Result: " << final_result << "\n";

// 启动多个异步任务
std::vector<std::future<int>> futures;
for (int i = 0; i < 5; ++i) {
    futures.push_back(std::async(std::launch::async, [i]() {
        return i * i;
    }));
}

// 收集结果
for (auto& f : futures) {
    std::cout << f.get() << " ";  // 0 1 4 9 16
}

场景五:即时排序和自定义比较

#include <algorithm>
#include <vector>
#include <string>
#include <iostream>

struct Person {
    std::string name;
    int age;
    double salary;
};

std::vector<Person> people = {
    {"Alice", 30, 75000},
    {"Bob", 25, 60000},
    {"Charlie", 35, 90000},
    {"Diana", 28, 75000},
};

// 按年龄排序
std::sort(people.begin(), people.end(),
    [](const Person& a, const Person& b) { return a.age < b.age; });

// 按薪水降序,薪水相同时按姓名升序
std::sort(people.begin(), people.end(),
    [](const Person& a, const Person& b) {
        if (a.salary != b.salary) return a.salary > b.salary;
        return a.name < b.name;
    });

// 按姓名长度排序
std::sort(people.begin(), people.end(),
    [](const Person& a, const Person& b) {
        return a.name.size() < b.name.size();
    });

场景六:高阶函数——返回 Lambda 的函数

// 返回一个"乘以 factor"的函数
auto make_multiplier(int factor) {
    return [factor](int x) { return x * factor; };
    // 注意:factor 被值捕获,即使 make_multiplier 返回后也安全
}

auto times3 = make_multiplier(3);
auto times10 = make_multiplier(10);

std::cout << times3(7);   // 21
std::cout << times10(7);  // 70

// 更复杂的高阶函数:创建一个"计数器"
auto make_counter(int start = 0) {
    return [count = start]() mutable { return count++; };
}

auto counter1 = make_counter(100);
auto counter2 = make_counter();

std::cout << counter1() << " " << counter1();  // 100 101
std::cout << counter2() << " " << counter2();  // 0 1

Java ↔ C++ Lambda 深度对比

全面对比表

维度 Java C++
语法 (参数) -> { 体 } [捕获](参数) { 体 }
底层实现 匿名内部类的语法糖,生成 .class 文件 编译器生成的匿名函数对象,可完全内联
类型 函数式接口(Function<T,R> 等) 编译器生成的唯一闭包类型(用 auto 承接)
捕获外部变量 只能读,变量必须 effectively final 值捕获(可 mutable 修改)、引用捕获、移动捕获
修改捕获变量 ❌ 不可修改 mutable 修改副本;引用捕获直接修改原变量
泛型参数 ❌ 不支持 ✅ C++14 [](auto x){}
移动语义 不适用(GC 管理) ✅ 表达式捕获支持移动
this 捕获 隐式持有外部类引用 [this](引用)或 [*this](拷贝,C++17)
存储到变量 赋值给函数式接口变量 autostd::function<签名>
作为参数 声明为函数式接口类型 std::function<签名>(灵活)或模板参数(高性能)
存储到容器 List<Consumer<String>> std::vector<std::function<void(int)>>
运行时开销 虚拟调用 + 可能的装箱 auto 时零开销(内联);std::function 有虚调用开销
序列化 ✅ 支持 ❌ 不支持(Lambda 不可序列化)
高阶函数 Function<Int, Function<Int, Int>> auto f = [](int x) { return [x](int y){...}; };
状态管理 闭包天然持有(无所有权问题) 需自行管理引用生命周期(悬垂引用风险)

关键差异解读

1. 捕获语义是最大的差异

// Java:effectively final,只能读
int factor = 10;
IntFunction<Integer> f = x -> x * factor;
// factor = 20;  // ❌ 编译错误:factor 必须 effectively final
// C++:五种捕获方式任选
int factor = 10;

// 方式1:值捕获(只读副本)
auto f1 = [factor](int x) { return x * factor; };

// 方式2:值捕获 + mutable(可修改副本)
auto f2 = [factor](int x) mutable { factor++; return x * factor; };

// 方式3:引用捕获(修改原变量)
auto f3 = [&factor](int x) { factor++; return x * factor; };

2. 生命周期的责任不同

Java 的 GC 让你无需担心 Lambda 中引用的对象何时被释放。C++ 要求你明确知道每个变量的生命周期:

// ⚠️ C++ 中必须小心的模式
std::function<int()> create_lambda() {
    int local = 42;

    // ❌ 引用捕获——返回后 local 已销毁
    // return [&local]() { return local; };

    // ✅ 值捕获——Lambda 持有 local 的副本
    return [local]() { return local; };

    // ✅ 移动捕获——同样安全
    auto ptr = std::make_unique<int>(42);
    return [p = std::move(ptr)]() { return *p; };
}

好习惯与坏味道

✅ 好习惯

① Lambda 体尽量短小,复杂逻辑提取为命名函数

// ❌ Lambda 体过长——难以阅读和测试
std::sort(data.begin(), data.end(), [](const auto& a, const auto& b) {
    // 20 行复杂的比较逻辑...
    if (a.category != b.category) {
        if (a.priority > 5) {
            // ... 15 行 ...
        }
    }
});

// ✅ 提取为命名函数(或函数对象)
bool compare_by_priority(const Item& a, const Item& b) {
    if (a.category != b.category) return a.category < b.category;
    return a.priority < b.priority;
}
std::sort(data.begin(), data.end(), compare_by_priority);

② 持有 Lambda 优先用 auto,只在需要类型擦除时用 std::function

// ✅ 优先:零开销,编译器可内联
auto add = [](int a, int b) { return a + b; };

// ⚠️ 仅在需要类型擦除时用(存储到容器、作为类成员等)
std::function<int(int, int)> stored_op;
std::vector<std::function<void()>> callbacks;

③ 给长 Lambda 起个有意义的名字

// ❌ 匿名 Lambda 嵌套在算法调用中——意图不明确
auto result = std::find_if(data.begin(), data.end(),
    [threshold](const auto& item) {
        return item.score > threshold && item.isActive() && !item.isExpired();
    });

// ✅ 给 Lambda 起名,意图自解释
auto is_qualified = [threshold](const auto& item) {
    return item.score > threshold && item.isActive() && !item.isExpired();
};
auto result = std::find_if(data.begin(), data.end(), is_qualified);

④ 返回 Lambda 时用值捕获,不用引用捕获

// ❌ 危险:返回的 Lambda 持有局部变量的引用
auto make_bad_multiplier(int factor) {
    return [&factor](int x) { return x * factor; };
    // factor 是参数(也是局部变量),函数返回后悬垂!
}

// ✅ 安全:值捕获
auto make_good_multiplier(int factor) {
    return [factor](int x) { return x * factor; };
}

⑤ 在 STL 算法中用 Lambda 代替手写循环

std::vector<int> data = {1, -2, 3, -4, 5};

// ❌ 手写循环——意图不清晰,容易出错
std::vector<int> positives;
for (size_t i = 0; i < data.size(); ++i) {
    if (data[i] > 0) {
        positives.push_back(data[i] * 2);
    }
}

// ✅ STL 算法 + Lambda——声明式,意图明确
std::vector<int> positives;
std::transform(data.begin(), data.end(), std::back_inserter(positives),
               [](int x) { return x > 0 ? x * 2 : x; });
// 或者分两步
std::copy_if(data.begin(), data.end(), std::back_inserter(positives),
             [](int x) { return x > 0; });

⑥ 用 const 引用捕获避免不必要拷贝

std::vector<std::string> names = {"Alice", "Bob", "Charlie", "Diana"};

// ❌ 值捕获大对象——每个字符串都拷贝一份
auto find_long = [names](size_t minLen) {
    return std::find_if(names.begin(), names.end(),
        [minLen](const std::string& s) { return s.size() >= minLen; });
};

// ✅ 引用捕获——零拷贝
auto find_long_fast = [&names](size_t minLen) {
    return std::find_if(names.begin(), names.end(),
        [minLen](const std::string& s) { return s.size() >= minLen; });
};
// ⚠️ 但要确保 names 在 Lambda 被调用时还活着

⑦ 用 Lambda 实现 scope guard 管理资源

// ✅ 利用 RAII + Lambda 确保清理
void process() {
    auto* res = acquire_resource();
    auto guard = [res]() { release_resource(res); };

    // 使用 res...
    if (some_error) return;  // guard 会在作用域结束时自动清理

    // 正常流程...
}  // 无论怎么退出,guard 都会执行

❌ 坏味道

① 引用捕获后 Lambda 逃逸出作用域

这是 C++ Lambda 最常见的 bug 来源:

// ❌ 经典错误:Lambda 的生命周期长于被捕获的变量
std::function<void()> setup_bad() {
    int counter = 0;
    auto lambda = [&counter]() {
        std::cout << ++counter;  // counter 的引用
    };
    return lambda;  // ❌ 返回后 counter 被销毁!
}

void schedule_work() {
    int task_id = 42;
    // ❌ 如果这个 Lambda 被延后执行,task_id 已不存在
    thread_pool.submit([&task_id]() {
        process(task_id);  // 悬垂引用!
    });
}

// ✅ 正确:值捕获或移动捕获
std::function<void()> setup_good() {
    int counter = 0;
    return [counter]() mutable {
        std::cout << ++counter;  // Lambda 持有自己的副本
    };
}

② 滥用 [=][&] 而不明确列出捕获内容

int a = 1, b = 2, c = 3, d = 4, e = 5;

// ❌ [=] 捕获了所有变量——Lambda 实际只用到 a 和 c
//   多余的捕获增加 Lambda 对象大小,且意图不明确
auto bad = [=]() { return a + c; };

// ✅ 明确列出需要的捕获
auto good = [a, c]() { return a + c; };

③ Lambda 内部做复杂的多重嵌套

// ❌ Lambda 套 Lambda,难以理解
auto result = std::find_if(data.begin(), data.end(),
    [&](const auto& outer) {
        return std::any_of(outer.items.begin(), outer.items.end(),
            [&](const auto& inner) {
                return std::all_of(inner.tags.begin(), inner.tags.end(),
                    [&](const auto& tag) {
                        return tag.isValid() && tag.weight > threshold;
                    });
            });
    });

// ✅ 拆分为命名的辅助 Lambda
auto is_valid_tag = [threshold](const auto& tag) {
    return tag.isValid() && tag.weight > threshold;
};
auto has_valid_tags = [&](const auto& inner) {
    return std::all_of(inner.tags.begin(), inner.tags.end(), is_valid_tag);
};
auto has_item_with_valid_tags = [&](const auto& outer) {
    return std::any_of(outer.items.begin(), outer.items.end(), has_valid_tags);
};

auto result = std::find_if(data.begin(), data.end(), has_item_with_valid_tags);

④ 在头文件中定义捕获大量变量的 Lambda

// ❌ 在头文件中:
// 每次包含头文件的翻译单元都生成新的 Lambda 类型
// 违反 ODR(One Definition Rule)风险
inline auto bad = [x = some_global]() { return x; };

// ✅ 函数返回 Lambda 或使用函数对象
inline auto get_lambda() {
    return [x = some_global]() { return x; };
}

⑤ 不使用 std::ref 而试图在 std::bind 中修改原对象

int value = 10;

// ❌ std::bind 默认拷贝参数——修改的是副本
auto bad_modifier = std::bind([](int& x) { x *= 2; }, value);
bad_modifier();
std::cout << value;  // 仍然是 10!副本被修改了

// ✅ 使用 std::ref 传递引用
auto good_modifier = std::bind([](int& x) { x *= 2; }, std::ref(value));
good_modifier();
std::cout << value;  // 20

⑥ Lambda 中捕获 this 后异步使用

class Worker {
    int state_ = 0;
public:
    void start_async() {
        // ❌ 危险:如果 Worker 对象在回调执行前被析构
        std::async(std::launch::async, [this]() {
            state_ = 42;  // this 可能已悬垂
        });
    }

    // ✅ 方案1:用 shared_from_this(需要继承 enable_shared_from_this)
    // ✅ 方案2:用 [*this](C++17)拷贝整个对象
    void start_async_safe() {
        std::async(std::launch::async, [*this]() {
            // state_ 现在是 Lambda 自己的副本
        });
    }
};

⑦ 过度使用 std::function 作为参数类型

// ❌ 每次调用都有虚函数开销
void for_each_bad(const std::vector<int>& v,
                   const std::function<void(int)>& callback) {
    for (int x : v) callback(x);
}

// ✅ 模板版本:零开销,可内联
template<typename F>
void for_each_good(const std::vector<int>& v, F&& callback) {
    for (int x : v) callback(x);
}

// 如果确实需要类型擦除(比如虚函数接口),用 std::function

⑧ 混淆值捕获和 mutable 的语义

int counter = 0;

auto f = [counter]() mutable {
    return ++counter;  // 修改的是 Lambda 内部的副本
};

f(); f(); f();
std::cout << counter;  // 0 —— 外部变量未变!
// 新手常以为 counter 会变成 3

// 如果你真的想修改外部变量,用引用捕获
auto g = [&counter]() { return ++counter; };
g(); g(); g();
std::cout << counter;  // 3

完整可运行代码示例

下面是一个综合示例,展示了本文讨论的几乎所有 Lambda 特性。你可以复制、编译并运行它:

// compile: g++ -std=c++17 -o lambda_demo lambda_demo.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>
#include <memory>
#include <future>
#include <string>
#include <map>

// ============================================================
// 1. 基础 Lambda
// ============================================================
void demo_basic() {
    std::cout << "=== 1. 基础 Lambda ===\n";

    // 最简 Lambda
    auto hello = []() { std::cout << "Hello, Lambda!\n"; };
    hello();

    // 带参数和返回类型
    auto add = [](int a, int b) -> int { return a + b; };
    std::cout << "3 + 4 = " << add(3, 4) << "\n";

    // 返回类型自动推导
    auto square = [](double x) { return x * x; };
    std::cout << "6.5² = " << square(6.5) << "\n\n";
}

// ============================================================
// 2. 捕获列表
// ============================================================
void demo_capture() {
    std::cout << "=== 2. 捕获列表 ===\n";

    int factor = 10;
    int sum = 0;

    // 值捕获
    auto multiply = [factor](int x) { return x * factor; };
    std::cout << "值捕获: 5 * " << factor << " = " << multiply(5) << "\n";

    // 引用捕获
    std::vector<int> v = {1, 2, 3, 4, 5};
    std::for_each(v.begin(), v.end(), [&sum](int x) { sum += x; });
    std::cout << "引用捕获累加: sum = " << sum << "\n";

    // mutable
    auto counter = [count = 0]() mutable { return count++; };
    std::cout << "mutable 计数器: " << counter() << ", "
              << counter() << ", " << counter() << "\n";

    // 隐式捕获
    int a = 1, b = 2, c = 3;
    auto sum_all = [=]() { return a + b + c; };
    std::cout << "隐式值捕获 [=]: " << sum_all() << "\n\n";
}

// ============================================================
// 3. 移动捕获(C++14)
// ============================================================
void demo_move_capture() {
    std::cout << "=== 3. 移动捕获 ===\n";

    // unique_ptr 移动捕获
    auto ptr = std::make_unique<int>(42);
    std::cout << "移动前 ptr 值: " << *ptr << "\n";

    auto owner = [p = std::move(ptr)]() {
        return *p;
    };

    std::cout << "Lambda 中的值: " << owner() << "\n";
    std::cout << "移动后 ptr 是否为 null: " << (ptr == nullptr ? "是" : "否") << "\n";

    // 大 vector 移动捕获
    std::vector<int> big_data(5);
    std::iota(big_data.begin(), big_data.end(), 100);
    std::cout << "移动前 big_data 大小: " << big_data.size() << "\n";

    auto consumer = [data = std::move(big_data)]() {
        std::cout << "Lambda 持有的数据: ";
        for (int x : data) std::cout << x << " ";
        std::cout << "\n";
    };

    consumer();
    std::cout << "移动后 big_data 大小: " << big_data.size() << "\n\n";
}

// ============================================================
// 4. 泛型 Lambda(C++14)
// ============================================================
void demo_generic() {
    std::cout << "=== 4. 泛型 Lambda ===\n";

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

    std::cout << "int:    " << universal_add(1, 2) << "\n";
    std::cout << "double: " << universal_add(1.5, 2.5) << "\n";
    std::cout << "string: " << universal_add(std::string("Hello "),
                                              std::string("World")) << "\n";

    // 泛型 Lambda 返回泛型 Lambda
    auto make_times = [](auto factor) {
        return [factor](auto x) { return x * factor; };
    };

    auto times2 = make_times(2);
    auto times_pi = make_times(3.14159);

    std::cout << "times2(10) = " << times2(10) << "\n";
    std::cout << "times_pi(2.0) = " << times_pi(2.0) << "\n\n";
}

// ============================================================
// 5. STL 算法 + Lambda
// ============================================================
void demo_stl_algorithms() {
    std::cout << "=== 5. STL 算法 + Lambda ===\n";

    std::vector<int> data = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};

    // 排序(自定义比较器:降序)
    std::sort(data.begin(), data.end(),
              [](int a, int b) { return a > b; });
    std::cout << "降序排序: ";
    for (int x : data) std::cout << x << " ";
    std::cout << "\n";

    // 筛选偶数
    std::vector<int> evens;
    std::copy_if(data.begin(), data.end(), std::back_inserter(evens),
                 [](int x) { return x % 2 == 0; });
    std::cout << "偶数: ";
    for (int x : evens) std::cout << x << " ";
    std::cout << "\n";

    // transform:平方
    std::vector<int> squared(data.size());
    std::transform(data.begin(), data.end(), squared.begin(),
                   [](int x) { return x * x; });
    std::cout << "平方: ";
    for (int x : squared) std::cout << x << " ";
    std::cout << "\n";

    // 查找第一个 > 5 的元素
    auto it = std::find_if(data.begin(), data.end(),
                           [](int x) { return x > 5; });
    if (it != data.end()) {
        std::cout << "第一个 > 5 的元素: " << *it << "\n";
    }

    // count_if
    int count_over_4 = std::count_if(data.begin(), data.end(),
                                     [](int x) { return x > 4; });
    std::cout << "> 4 的元素个数: " << count_over_4 << "\n\n";
}

// ============================================================
// 6. std::function
// ============================================================
void demo_std_function() {
    std::cout << "=== 6. std::function ===\n";

    // 存储不同类型的可调用对象
    std::function<int(int, int)> op;

    op = [](int a, int b) { return a + b; };
    std::cout << "Lambda:  10 + 3 = " << op(10, 3) << "\n";

    op = std::multiplies<int>{};
    std::cout << "multiplies: 10 * 3 = " << op(10, 3) << "\n";

    // 容器中存储多个操作
    std::vector<std::function<int(int)>> transformations;
    transformations.push_back([](int x) { return x + 1; });
    transformations.push_back([](int x) { return x * 2; });
    transformations.push_back([](int x) { return x * x; });

    int value = 5;
    for (auto& t : transformations) {
        std::cout << "transform(" << value << ") = " << t(value) << "\n";
    }
    std::cout << "\n";
}

// ============================================================
// 7. 高阶函数
// ============================================================
void demo_higher_order() {
    std::cout << "=== 7. 高阶函数 ===\n";

    // 返回 Lambda 的函数
    auto make_adder = [](int base) {
        return [base](int x) { return base + x; };
    };

    auto add5 = make_adder(5);
    auto add100 = make_adder(100);

    std::cout << "add5(10) = " << add5(10) << "\n";
    std::cout << "add100(10) = " << add100(10) << "\n";

    // 管道组合
    auto compose = [](auto f, auto g) {
        return [f, g](auto x) { return f(g(x)); };
    };

    auto add1 = [](int x) { return x + 1; };
    auto times2 = [](int x) { return x * 2; };

    auto add1_then_times2 = compose(times2, add1);
    auto times2_then_add1 = compose(add1, times2);

    std::cout << "add1 ∘ times2 (5) = " << add1_then_times2(5) << "  (5*2+1)\n";
    std::cout << "times2 ∘ add1 (5) = " << times2_then_add1(5) << "  ((5+1)*2)\n\n";
}

// ============================================================
// 8. this 捕获(C++17 演示 *this)
// ============================================================
class Counter {
    int value_ = 0;
public:
    explicit Counter(int start) : value_(start) {}

    // C++11: [this] 引用捕获
    auto get_reader_ref() {
        return [this]() { return value_; };
    }

    // C++17: [*this] 拷贝捕获
    auto get_reader_copy() {
        return [*this]() { return value_; };
    }

    void increment() { value_++; }
};

void demo_this_capture() {
    std::cout << "=== 8. this / *this 捕获 ===\n";

    Counter counter(10);

    auto ref_reader = counter.get_reader_ref();
    auto copy_reader = counter.get_reader_copy();

    std::cout << "修改前: ref=" << ref_reader()
              << " copy=" << copy_reader() << "\n";

    counter.increment();
    counter.increment();

    std::cout << "修改后: ref=" << ref_reader()
              << " copy=" << copy_reader() << "\n";
    std::cout << "  [this] 反映了修改, [*this] 是独立副本\n\n";
}

// ============================================================
// 9. 异步任务
// ============================================================
void demo_async() {
    std::cout << "=== 9. 异步任务 ===\n";

    // 启动异步计算
    auto future = std::async(std::launch::async, []() {
        int sum = 0;
        for (int i = 1; i <= 100; ++i) sum += i;
        return sum;
    });

    std::cout << "主线程继续工作...\n";

    // 获取结果
    int result = future.get();
    std::cout << "1+2+...+100 = " << result << "\n\n";
}

// ============================================================
// 10. RAII Scope Guard
// ============================================================
class ScopeGuard {
    std::function<void()> onExit_;
public:
    explicit ScopeGuard(std::function<void()> f) : onExit_(std::move(f)) {}
    ~ScopeGuard() { if (onExit_) onExit_(); }
    ScopeGuard(const ScopeGuard&) = delete;
    ScopeGuard& operator=(const ScopeGuard&) = delete;
};

void demo_scope_guard() {
    std::cout << "=== 10. Scope Guard ===\n";

    auto guarded_function = []() {
        std::cout << "  进入作用域\n";
        ScopeGuard guard([]() {
            std::cout << "  离开作用域——自动清理!\n";
        });
        std::cout << "  在作用域内工作...\n";
    };

    guarded_function();
    std::cout << "\n";
}

// ============================================================
// main
// ============================================================
int main() {
    std::cout << "╔══════════════════════════════════════╗\n";
    std::cout << "║   C++ Lambda 表达式综合演示          ║\n";
    std::cout << "╚══════════════════════════════════════╝\n\n";

    demo_basic();
    demo_capture();
    demo_move_capture();
    demo_generic();
    demo_stl_algorithms();
    demo_std_function();
    demo_higher_order();
    demo_this_capture();
    demo_async();
    demo_scope_guard();

    return 0;
}

这个示例涵盖了:

  • 基础 Lambda 语法
  • 值捕获、引用捕获、mutable
  • C++14 移动捕获(unique_ptr、大 vector
  • 泛型 Lambda(auto 参数)
  • STL 算法集成(sortcopy_iftransformfind_ifcount_if
  • std::function 类型擦除
  • 高阶函数(返回 Lambda、函数组合)
  • [this] vs [*this]
  • 异步任务(std::async
  • RAII Scope Guard

总结

C++ 的 Lambda 表达式远不止是"匿名函数"的语法糖——它是现代 C++ 函数式编程范式的基石。对于从 Java 转来的开发者,这里有几个关键要点:

  1. 捕获列表是灵魂:Java 只有 effectively final 的隐式捕获,C++ 给你五种显式选择——值、引用、移动、this、表达式。每种都有明确的所有权语义。

  2. 零开销抽象:用 auto 承接的 Lambda,编译器可以完全内联,性能等同手写代码。这与 Java 的虚调用 + 可能的装箱形成鲜明对比。

  3. 泛型 Lambda 是独特优势:C++14 的 [](auto x){} 让你可以写出真正泛型的匿名函数——Java 至今做不到。

  4. 生命周期需自行管理:没有 GC,引用捕获可能导致悬垂引用。好在值捕获和移动捕获提供了安全的选择。

  5. std::function 是双刃剑:它提供了类型擦除的灵活性(类似 Java 的函数式接口),但有运行时开销。性能敏感处用模板或 auto

  6. Lambda + STL = 威力倍增std::sortstd::copy_ifstd::transformstd::find_if——这些算法配上 Lambda,让你写出的代码既简洁又高效。

  7. 用 Lambda 实现高阶模式:返回 Lambda 的函数、函数组合、scope guard——这些在 Java 中需要额外设计模式的东西,在 C++ 中可以用 Lambda 自然表达。

掌握 Lambda,你才算真正入门了现代 C++。下一篇文章我们将学习 C++ 的并发编程——包括 std::threadstd::mutexstd::atomic 和异步任务,到时候 Lambda 会再次大显身手。


评论