Skip to content

Ch 13: Lambda 表达式

  • 理解 Lambda 表达式的基本语法和本质
  • 掌握 Lambda 的各种捕获模式及适用场景
  • 学会在算法和回调中使用 Lambda
  • 理解 Lambda 和 std::function 的关系和性能差异
  • 理解 C++20 Lambda 的增强功能
# Python - Lambda 是匿名函数
square = lambda x: x ** 2
print(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 lambda
points = [(1, 2), (3, 1), (2, 5)]
points.sort(key=lambda p: p[1]) # 按 y 排序
# Python - 闭包自动捕获变量
def make_multiplier(factor):
# factor 被内层函数捕获
return lambda x: x * factor
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(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()) # 1
print(c()) # 2
// C++ Lambda - [capture] (params) -> ret { body }
// 最简单的 Lambda
auto 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; // 3
std::cout << add(1.5, 2.5) << std::endl; // 4.0
std::cout << add(std::string("a"), "b") << std::endl; // "ab"
#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
}
#include <iostream>
// 函数指针
int (*square_fn)(int) = [](int x) { return x * x; };
// Lambda(类型由编译器生成)
auto square_lambda = [](int x) { return x * x; };
// 函数指针只能用于无状态 Lambda
int (*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
}
#include <vector>
#include <algorithm>
#include <iostream>
// 直接传递 Lambda 给算法
std::vector<int> nums = {5, 2, 8, 1, 9, 3};
// 排序用 Lambda
std::sort(nums.begin(), nums.end(), [](int a, int b) {
return a > b; // 降序
});
// 查找用 Lambda
auto it = std::find_if(nums.begin(), nums.end(), [](int x) {
return x > 5;
});
std::cout << "First > 5: " << (it != nums.end() ? *it : -1) << std::endl;
int a = 1, b = 2, c = 3;
auto lambda1 = []() { /* 不捕获任何变量 */ };
auto lambda2 = [a] { return a; }; // 按值捕获 a
auto lambda3 = [&a] { return a; }; // 按引用捕获 a
auto lambda4 = [=] { return a + b + c; }; // 按值捕获所有变量
auto lambda5 = [&] { return a + b + c; }; // 按引用捕获所有变量
auto lambda6 = [=, &b] { return a + b; }; // 按值捕获所有,但按引用捕获 b
auto lambda7 = [&, c] { return a + b + c; }; // 按引用捕获所有,但按值捕获 c
# 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
#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;
}
#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;
}
#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
});
// ❌ 危险:返回捕获引用的 Lambda
auto 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 是拷贝,不影响原变量
};
}
#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;
}
#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;
}
#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;
}
#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;
}
}
#include <algorithm>
#include <iostream>
#include <vector>
// 危险:捕获局部变量的引用
auto create_filter = [](int threshold) {
// 返回的 Lambda 捕获了 threshold 的引用
// 但 threshold 是值传递的,Lambda 内部有拷贝
return [threshold](int x) { return x > threshold; };
// 注意:这里是按值捕获,所以是安全的
};
// 危险示例:返回捕获引用的 Lambda
auto make_dangerous = [&]() {
int local = 42;
return [&]() { return local; }; // ❌ 危险!local 生命周期结束
};
// auto fn = make_dangerous(); // 悬空引用!
#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;
}
#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;
}
#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;
}
#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;
}
#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::function
callbacks.push_back([]() { std::cout << "A\n"; });
callbacks.push_back([]() { std::cout << "B\n"; });
for (auto& cb : callbacks) cb();
// C++20 - 模板 Lambda
auto generic_add = []<typename T>(T a, T b) { return a + b; };
// 使用
int i = generic_add(1, 2); // int
double d = generic_add(1.5, 2.5); // double
std::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 可以指定约束
#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
}
// C++20 - Lambda 可以是 constexpr
constexpr 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;
}
#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;
}
#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;
}
特性说明
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 LambdaC++20,可在编译期求值

下章预告:ch14 学习 std::vector,这是 C++ 最常用的动态数组容器。