Ch 13: Lambda 表达式
- 理解 Lambda 表达式的基本语法和本质
- 掌握 Lambda 的各种捕获模式及适用场景
- 学会在算法和回调中使用 Lambda
- 理解 Lambda 和 std::function 的关系和性能差异
- 理解 C++20 Lambda 的增强功能
13.1 Python Lambda 回顾
Section titled “13.1 Python Lambda 回顾”Lambda 基础
Section titled “Lambda 基础”# Python - Lambda 是匿名函数square = lambda x: x ** 2print(square(5)) # 25
# Lambda 用于高阶函数numbers = [1, 2, 3, 4, 5]doubled = list(map(lambda x: x * 2, numbers)) # [2, 4, 6, 8, 10]filtered = list(filter(lambda x: x > 2, numbers)) # [3, 4, 5]
# sort with lambdapoints = [(1, 2), (3, 1), (2, 5)]points.sort(key=lambda p: p[1]) # 按 y 排序Python 闭包
Section titled “Python 闭包”# Python - 闭包自动捕获变量def make_multiplier(factor): # factor 被内层函数捕获 return lambda x: x * factor
double = make_multiplier(2)triple = make_multiplier(3)
print(double(5)) # 10print(triple(5)) # 15
# nonlocal 关键字修改外层变量def make_counter(start=0): count = start def counter(): nonlocal count count += 1 return count return counter
c = make_counter(0)print(c()) # 1print(c()) # 213.2 C++ Lambda 基础
Section titled “13.2 C++ Lambda 基础”// C++ Lambda - [capture] (params) -> ret { body }
// 最简单的 Lambdaauto hello = []() { std::cout << "Hello!\n"; };hello(); // 输出 "Hello!"
// 带参数auto square = [](int x) { return x * x; };std::cout << square(5) << std::endl; // 25
// 带返回类型auto is_even = [](int x) -> bool { return x % 2 == 0; };
// 泛型 Lambda(C++14)auto add = [](auto a, auto b) { return a + b; };std::cout << add(1, 2) << std::endl; // 3std::cout << add(1.5, 2.5) << std::endl; // 4.0std::cout << add(std::string("a"), "b") << std::endl; // "ab"Lambda 的本质
Section titled “Lambda 的本质”#include <typeinfo>#include <iostream>
int main() { auto lambda = [](int x) { return x * 2; };
// Lambda 不是函数指针,但可以转换为函数指针(无捕获时) using FnType = int(*)(int); FnType fn_ptr = lambda; // OK,因为 lambda 无状态
// Lambda 的类型是编译器生成的匿名类(闭包类型) std::cout << "Lambda callable\n";
// 调用 std::cout << lambda(5) << std::endl; // 10 std::cout << fn_ptr(5) << std::endl; // 10}Lambda vs 函数指针
Section titled “Lambda vs 函数指针”#include <iostream>
// 函数指针int (*square_fn)(int) = [](int x) { return x * x; };
// Lambda(类型由编译器生成)auto square_lambda = [](int x) { return x * x; };
// 函数指针只能用于无状态 Lambdaint (*fp)(int) = [](int x) { return x * x; }; // OK,无状态// int (*fp2)(int) = [y](int x) { return x * y; }; // ❌ 有捕获,不能转为指针
int main() { std::cout << square_fn(5) << std::endl; // 25 std::cout << square_lambda(5) << std::endl; // 25}Lambda 作为参数
Section titled “Lambda 作为参数”#include <vector>#include <algorithm>#include <iostream>
// 直接传递 Lambda 给算法std::vector<int> nums = {5, 2, 8, 1, 9, 3};
// 排序用 Lambdastd::sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; // 降序});
// 查找用 Lambdaauto it = std::find_if(nums.begin(), nums.end(), [](int x) { return x > 5;});
std::cout << "First > 5: " << (it != nums.end() ? *it : -1) << std::endl;13.3 Lambda 捕获详解
Section titled “13.3 Lambda 捕获详解”捕获模式一览
Section titled “捕获模式一览”int a = 1, b = 2, c = 3;
auto lambda1 = []() { /* 不捕获任何变量 */ };auto lambda2 = [a] { return a; }; // 按值捕获 aauto lambda3 = [&a] { return a; }; // 按引用捕获 aauto lambda4 = [=] { return a + b + c; }; // 按值捕获所有变量auto lambda5 = [&] { return a + b + c; }; // 按引用捕获所有变量auto lambda6 = [=, &b] { return a + b; }; // 按值捕获所有,但按引用捕获 bauto lambda7 = [&, c] { return a + b + c; }; // 按引用捕获所有,但按值捕获 cPython 对比
Section titled “Python 对比”# Python - 所有捕获都是引用(对于可变对象)x = 10
def add_x(y): return x + y # 捕获 x
# nonlocal 才能修改def make_counter(): count = 0 def counter(): nonlocal count count += 1 return count return counter按值捕获 [x]
Section titled “按值捕获 [x]”#include <iostream>
int main() { int x = 10;
// 按值捕获 - 创建 Lambda 时拷贝 x auto lambda = [x](int a) { return a + x; };
x = 20; // 修改原变量,不影响 Lambda 内部
std::cout << lambda(5) << std::endl; // 15(使用捕获时的 x=10) return 0;}按引用捕获 [&x]
Section titled “按引用捕获 [&x]”#include <iostream>
int main() { int x = 10;
// 按引用捕获 - Lambda 内部操作原变量 auto lambda = [&x](int a) { x = a; // 修改原变量 return x; };
lambda(5); std::cout << x << std::endl; // 5
return 0;}值捕获 vs 引用捕获的选择
Section titled “值捕获 vs 引用捕获的选择”#include <vector>#include <algorithm>
std::vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6};
// ✅ 场景 1:只读访问,用 [=] 或 [threshold]int threshold = 3;auto greater_than = [threshold](int x) { return x > threshold; };auto count = std::count_if(nums.begin(), nums.end(), greater_than);
// ✅ 场景 2:需要累积结果,用 [&](小心 dangling)int sum = 0;std::for_each(nums.begin(), nums.end(), [&sum](int x) { if (x > 3) sum += x; // 引用捕获,可以修改 sum});
// ❌ 危险:返回捕获引用的 Lambdaauto get_lambda = [&]() -> std::function<int()> { int local = 10; return [&]() { return local; }; // 危险!local 在函数结束后销毁}// auto fn = get_lambda(); // 悬空引用!#include <iostream>
int main() { int a = 1, b = 2, c = 3;
// 混合捕获:按值捕获 a,按引用捕获 b,捕获所有 c auto lambda = [a, &b, c]() { std::cout << "a=" << a << ", b=" << b << ", c=" << c << std::endl; };
lambda();
// 按值捕获所有,但 b 按引用 auto lambda2 = [=, &b](int x) { b = x; // 修改原 b // a 和 c 是拷贝,不影响原变量 };}移动捕获(C++14)
Section titled “移动捕获(C++14)”#include <memory>#include <vector>#include <iostream>
int main() { // C++14 支持移动捕获
// 获取堆对象的所有权 auto vec = std::make_unique<std::vector<int>>(std::vector<int>{1, 2, 3});
// 移动捕获 - vec 被移入 Lambda,不再拥有所有权 auto lambda = [v = std::move(vec)](int multiplier) { std::vector<int> result; result.reserve(v->size()); for (int x : *v) { result.push_back(x * multiplier); } return result; };
// vec 已被移走,lambda 持有数据 auto result = lambda(10); for (int x : result) std::cout << x << " "; // 10 20 30 std::cout << std::endl;
// vec 现在是空壳 // std::cout << vec->size() << std::endl; // 错误!vec 已为空
return 0;}mutable 修饰符
Section titled “mutable 修饰符”#include <iostream>
int main() { int value = 10;
// 按值捕获的 Lambda,默认不能修改捕获的变量 auto lambda = [value]() { // value = 20; // ❌ 错误!value 是 const return value; };
// 使用 mutable 允许修改捕获的副本 auto mutable_lambda = [value]() mutable { value = 20; // OK,修改的是副本 return value; };
std::cout << "lambda(): " << lambda() << std::endl; // 10(原始 value) std::cout << "mutable_lambda(): " << mutable_lambda() << std::endl; // 20 std::cout << "value after: " << value << std::endl; // 10(未改变)
return 0;}13.4 Lambda 在算法中使用
Section titled “13.4 Lambda 在算法中使用”常用算法示例
Section titled “常用算法示例”#include <vector>#include <algorithm>#include <numeric>#include <iostream>
int main() { std::vector<int> nums = {1, 2, 3, 4, 5};
// for_each - 遍历并操作 std::cout << "Original: "; std::for_each(nums.begin(), nums.end(), [](int x) { std::cout << x << " "; }); std::cout << std::endl;
// count_if - 计数 auto evens = std::count_if(nums.begin(), nums.end(), [](int x) { return x % 2 == 0; }); std::cout << "Even count: " << evens << std::endl; // 2
// find_if - 查找 auto it = std::find_if(nums.begin(), nums.end(), [](int x) { return x > 3; }); if (it != nums.end()) { std::cout << "First > 3: " << *it << std::endl; // 4 }
// transform - 转换 std::vector<int> squares; squares.reserve(nums.size()); std::transform(nums.begin(), nums.end(), std::back_inserter(squares), [](int x) { return x * x; }); // squares = {1, 4, 9, 16, 25}
// remove_if - 删除 std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9}; data.erase(std::remove_if(data.begin(), data.end(), [](int x) { return x % 2 == 0; }), data.end()); // data = {1, 3, 5, 7, 9}
// sort - 排序 std::vector<int> unsorted = {5, 2, 8, 1, 9, 3}; std::sort(unsorted.begin(), unsorted.end(), [](int a, int b) { return a > b; // 降序 }); // unsorted = {9, 8, 5, 3, 2, 1}
// accumulate - 累积 int sum = std::accumulate(nums.begin(), nums.end(), 0, [](int acc, int x) { return acc + x; }); std::cout << "Sum: " << sum << std::endl; // 15
// 链式操作(C++20 ranges 更方便) std::vector<int> result; std::copy_if(nums.begin(), nums.end(), std::back_inserter(result), [](int x) { return x > 2; }); std::transform(result.begin(), result.end(), result.begin(), [](int x) { return x * 2; }); // result = {6, 8, 10}
return 0;}自定义比较器
Section titled “自定义比较器”#include <vector>#include <algorithm>#include <string>#include <iostream>
struct Person { std::string name; int age; std::string city;};
int main() { std::vector<Person> people = { {"Alice", 30, "NYC"}, {"Bob", 25, "LA"}, {"Charlie", 35, "NYC"}, {"Diana", 28, "LA"} };
// 按年龄降序 std::sort(people.begin(), people.end(), [](const Person& a, const Person& b) { return a.age > b.age; });
// 按城市分组,再按年龄排序 std::stable_sort(people.begin(), people.end(), [](const Person& a, const Person& b) { if (a.city != b.city) return a.city < b.city; return a.age < b.age; });
// 查找 auto it = std::find_if(people.begin(), people.end(), [](const Person& p) { return p.name == "Bob"; }); if (it != people.end()) { std::cout << "Found: " << it->name << ", " << it->age << std::endl; }}Lambda 捕获在算法中的陷阱
Section titled “Lambda 捕获在算法中的陷阱”#include <algorithm>#include <iostream>#include <vector>
// 危险:捕获局部变量的引用auto create_filter = [](int threshold) { // 返回的 Lambda 捕获了 threshold 的引用 // 但 threshold 是值传递的,Lambda 内部有拷贝 return [threshold](int x) { return x > threshold; }; // 注意:这里是按值捕获,所以是安全的};
// 危险示例:返回捕获引用的 Lambdaauto make_dangerous = [&]() { int local = 42; return [&]() { return local; }; // ❌ 危险!local 生命周期结束};// auto fn = make_dangerous(); // 悬空引用!13.5 std::function 详解
Section titled “13.5 std::function 详解”std::function 基础
Section titled “std::function 基础”#include <functional>#include <iostream>
int add(int a, int b) { return a + b; }int multiply(int a, int b) { return a * b; }
int main() { // std::function 是类型擦除的可调用对象包装器 std::function<int(int, int)> operation;
// 赋值函数指针 operation = add; std::cout << operation(3, 4) << std::endl; // 7
// 赋值 Lambda operation = [](int a, int b) { return a - b; }; std::cout << operation(3, 4) << std::endl; // -1
// 重新赋值 operation = multiply; std::cout << operation(3, 4) << std::endl; // 12
// 空检查 std::function<int(int, int)> empty_fn; if (!empty_fn) { std::cout << "Function is empty\n"; }
return 0;}函数对象(仿函数)
Section titled “函数对象(仿函数)”#include <functional>#include <iostream>
// 函数对象(仿函数)struct Multiply { int factor; Multiply(int f) : factor(f) {}
int operator()(int x) const { return x * factor; }};
struct Add { int operator()(int a, int b) const { return a + b; }};
int main() { std::function<int(int)> transform;
transform = Multiply(10); std::cout << transform(5) << std::endl; // 50
transform = [](int x) { return x + 1; }; std::cout << transform(5) << std::endl; // 6
std::function<int(int, int)> binary_op = Add{}; std::cout << binary_op(3, 4) << std::endl; // 7
return 0;}std::function 作为函数参数
Section titled “std::function 作为函数参数”#include <functional>#include <vector>#include <iostream>
// 使用 std::function 作为函数参数(类似 Python 的 callable)void apply(const std::vector<int>& nums, std::function<int(int)> transform) { for (int x : nums) { std::cout << transform(x) << " "; } std::cout << std::endl;}
// 模板版本(更高效)template<typename F>void apply_template(const std::vector<int>& nums, F transform) { for (int x : nums) { std::cout << transform(x) << " "; } std::cout << std::endl;}
int main() { std::vector<int> nums = {1, 2, 3};
// 传入 Lambda apply(nums, [](int x) { return x * 2; }); // 2 4 6 apply(nums, [](int x) { return x * x; }); // 1 4 9
// 捕获的 Lambda int multiplier = 10; apply(nums, [multiplier](int x) { return x * multiplier; }); // 10 20 30
// 模板版本更通用 apply_template(nums, [](int x) { return x + 1; }); // 2 3 4
return 0;}std::function 的性能开销
Section titled “std::function 的性能开销”#include <functional>#include <chrono>#include <iostream>
// 普通函数(零开销)int plain_function(int x) { return x * 2; }
// Lambda 转为函数指针(零开销)auto lambda_as_fn_ptr = [](int x) { return x * 2; };
// std::function(有虚调用开销)std::function<int(int)> fn_object = [](int x) { return x * 2; };
int main() { const int iterations = 100000000;
// 测量普通函数 auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; ++i) { plain_function(i); } auto end = std::chrono::high_resolution_clock::now(); std::cout << "Plain function: " << std::chrono::duration<double>(end - start).count() << "s\n";
// 测量 Lambda 函数指针 start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; ++i) { lambda_as_fn_ptr(i); } end = std::chrono::high_resolution_clock::now(); std::cout << "Lambda as fn ptr: " << std::chrono::duration<double>(end - start).count() << "s\n";
// 测量 std::function start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; ++i) { fn_object(i); } end = std::chrono::high_resolution_clock::now(); std::cout << "std::function: " << std::chrono::duration<double>(end - start).count() << "s\n";
return 0;}std::function vs 模板参数
Section titled “std::function vs 模板参数”#include <vector>#include <functional>
// 使用 std::function - 有类型擦除开销,但接口清晰void process_function(std::function<void()> fn) { fn();}
// 使用模板参数 - 无开销,但会实例化多份代码template<typename F>void process_template(F fn) { fn();}
// 通用建议:优先用模板,除非需要:// 1. 存储在容器中(需要统一类型)// 2. 需要空状态检查// 3. 需要运行时决定类型
std::vector<std::function<void()>> callbacks; // 必须用 std::functioncallbacks.push_back([]() { std::cout << "A\n"; });callbacks.push_back([]() { std::cout << "B\n"; });for (auto& cb : callbacks) cb();13.6 C++20 Lambda 增强
Section titled “13.6 C++20 Lambda 增强”模板 Lambda(C++20)
Section titled “模板 Lambda(C++20)”// C++20 - 模板 Lambdaauto generic_add = []<typename T>(T a, T b) { return a + b; };
// 使用int i = generic_add(1, 2); // intdouble d = generic_add(1.5, 2.5); // doublestd::string s = generic_add(std::string("a"), "b"); // std::string
// C++14 的 auto 参数不是真正的模板auto cpp14_add = [](auto a, auto b) { return a + b; };// C++20 模板 Lambda 可以指定约束捕获时初始化(C++20)
Section titled “捕获时初始化(C++20)”#include <iostream>
int main() { // C++20 - 捕获时进行计算 int x = 10; auto lambda = [y = x * 2](int z) { return y + z; }; std::cout << lambda(5) << std::endl; // 25
// 可以用于移动语义 auto vec = std::make_unique<std::vector<int>>(std::vector<int>{1, 2, 3}); auto lambda2 = [v = std::move(vec)](int multiplier) { std::vector<int> result; for (int x : *v) result.push_back(x * multiplier); return result; };
// 默认初始化 struct Config { int timeout = 30; int retries = 3; };
auto lambda3 = [config = Config{}](int x) mutable { config.timeout += x; return config.timeout; }; std::cout << lambda3(5) << std::endl; // 35}constexpr Lambda(C++20)
Section titled “constexpr Lambda(C++20)”// C++20 - Lambda 可以是 constexprconstexpr auto square = []<typename T>(T x) { return x * x; };
// 编译期求值static_assert(square(5) == 25);static_assert(square(3.14) > 9.8 && square(3.14) < 10.0);
// 数组大小int arr[square(4)]; // int arr[16]
// 模板参数template<int N>constexpr int get_square() { return square(N);}
static_assert(get_square<7>() == 49);Lambda 表达式作为模板参数(C++20)
Section titled “Lambda 表达式作为模板参数(C++20)”#include <iostream>
int main() { // C++20 允许直接传递 Lambda 给模板 auto result = []<typename T>(T x) { return x * 2; }(5); std::cout << result << std::endl; // 10
// 更实用的例子 []<typename T>(T x, T y) { return x + y; }.template operator()<int>(3, 4); // 调用模板 operator()
return 0;}this 域的 Lambda(C++20)
Section titled “this 域的 Lambda(C++20)”#include <iostream>#include <vector>#include <algorithm>
class Processor {private: std::vector<int> data_; int multiplier_;
public: Processor(int mult) : multiplier_(mult) {}
void process() { // C++20 可以在 Lambda 中显式使用 this std::for_each(data_.begin(), data_.end(), [self = this](int x) { // self 显式捕获 self->process_item(x); }); }
void process_item(int x) { std::cout << x * multiplier_ << " "; }
void add(int x) { data_.push_back(x); }};
int main() { Processor p(10); p.add(1); p.add(2); p.add(3); p.process(); // 10 20 30 std::cout << std::endl;}13.7 完整示例:事件系统
Section titled “13.7 完整示例:事件系统”#include <functional>#include <map>#include <string>#include <iostream>#include <vector>
// 简单事件系统(类似 Python 的事件回调)class EventEmitter {private: std::map<std::string, std::vector<std::function<void()>>> listeners_;
public: // 注册事件监听器 void on(const std::string& event, std::function<void()> callback) { listeners_[event].push_back(std::move(callback)); }
// 触发事件 void emit(const std::string& event) { if (auto it = listeners_.find(event); it != listeners_.end()) { for (auto& listener : it->second) { listener(); } } }
// 带数据的触发 template<typename T> void emit(const std::string& event, T data) { if (auto it = listeners_.find(event); it != listeners_.end()) { for (auto& listener : it->second) { // 这里需要包装器,实际用法见下方 } } }
// 统计监听器数量 size_t listener_count(const std::string& event) const { if (auto it = listeners_.find(event); it != listeners_.end()) { return it->second.size(); } return 0; }};
// 带参数的事件系统template<typename... Args>class TypedEventEmitter {private: std::map<std::string, std::vector<std::function<void(Args...)>>> listeners_;
public: void on(const std::string& event, std::function<void(Args...)> callback) { listeners_[event].push_back(std::move(callback)); }
void emit(const std::string& event, Args... args) { if (auto it = listeners_.find(event); it != listeners_.end()) { for (auto& listener : it->second) { listener(args...); } } }};
int main() { EventEmitter emitter;
// 使用 Lambda 作为回调 int click_count = 0; emitter.on("click", [&click_count]() { ++click_count; std::cout << "Clicked! Count: " << click_count << std::endl; });
emitter.on("click", []() { std::cout << "Another handler!\n"; });
emitter.on("hover", []() { std::cout << "Hovered!\n"; });
// 触发事件 emitter.emit("click"); // Clicked! Count: 1, Another handler! emitter.emit("click"); // Clicked! Count: 2, Another handler! emitter.emit("hover"); // Hovered!
std::cout << "Click listeners: " << emitter.listener_count("click") << std::endl;
// 带参数的事件 TypedEventEmitter<std::string, int> typed; typed.on("data", [](const std::string& name, int value) { std::cout << name << ": " << value << std::endl; });
typed.emit("data", "Temperature", 25); // Temperature: 25
return 0;}13.8 章节总结
Section titled “13.8 章节总结”| 特性 | 说明 |
|---|---|
| Lambda 语法 | [capture] (params) -> ret { body } |
按值捕获 [x] | 捕获时拷贝,Lambda 内不修改原变量 |
按引用捕获 [&x] | 捕获引用,可修改原变量 |
混合捕获 [=, &x] | 按值捕获所有,但某个按引用 |
移动捕获 [x = std::move(v)] | C++14,移动所有权 |
| mutable | 允许修改按值捕获的副本 |
| std::function | 类型擦除,存储任何可调用对象 |
| 模板参数 | 无类型擦除开销,更高效 |
| C++20 模板 Lambda | []<typename T>(T x) |
| constexpr Lambda | C++20,可在编译期求值 |
下章预告:ch14 学习 std::vector,这是 C++ 最常用的动态数组容器。