跳到主要内容

C++ Lambda表达式深度解析

一、Lambda基础

1.1 什么是Lambda

Lambda表达式是C++11引入的匿名函数,用于就地定义函数对象。

// 传统函数对象
struct Add {
int operator()(int a, int b) {
return a + b;
}
};

Add add;
int result1 = add(3, 4); // 7

// Lambda表达式
auto addLambda = [](int a, int b) {
return a + b;
};
int result2 = addLambda(3, 4); // 7

1.2 Lambda语法

[capture](parameters) -> return_type {
// 函数体
}

各部分说明

// 完整语法示例
auto lambda = [capture](int a, int b) -> int {
return a + b;
};

// 简化语法(自动推导返回类型)
auto lambda2 = [](int a, int b) {
return a + b;
};

1.3 基本示例

// 无参数
auto sayHello = []() {
std::cout << "Hello!" << std::endl;
};

// 有参数
auto add = [](int a, int b) {
return a + b;
};

// 使用
sayHello();
int result = add(3, 5); // 8

二、捕获机制

2.1 捕获方式

捕获方式语法说明
无捕获[]不捕获外部变量
按值捕获[x, y]复制变量
按引用捕获[&x, &y]引用变量
混合捕获[x, &y]混合方式
全部按值[=]所有变量按值
全部按引用[&]所有变量按引用
初始化捕获[x = expr]C++14

2.2 按值捕获

int x = 10;
int y = 20;

// 按值捕获
auto lambda1 = [x, y](int z) {
// x和y是副本,不能修改
return x + y + z;
};

int result = lambda1(5); // 35
// x仍然是10,y仍然是20

2.3 按引用捕获

int x = 10;
int y = 20;

// 按引用捕获
auto lambda2 = [&x, &y]() {
x += 5; // 修改外部变量
y += 5;
};

lambda2();
// x = 15, y = 25

2.4 混合捕获

int x = 10;
int y = 20;
int z = 30;

// x按值,y按引用
auto lambda = [x, &y](int a) {
return x + y + a; // x只读,y可修改
};

2.5 默认捕获

int x = 10;
int y = 20;

// 全部按值
auto lambda1 = [=]() {
return x + y; // x和y是副本
};

// 全部按引用
auto lambda2 = [&]() {
x++; // 可以修改
y++;
return x + y;
};

2.6 mutable关键字

int x = 10;

// 默认:按值捕获的变量是const的
auto lambda1 = [x]() {
// x++; // 编译错误
return x;
};

// 使用mutable:可以修改按值捕获的副本
auto lambda2 = [x]() mutable {
x++; // 修改副本,不影响外部x
return x;
};

int result1 = lambda2(); // 11
int result2 = lambda2(); // 12
// 外部x仍然是10

三、返回类型

3.1 自动推导

// 自动推导返回类型
auto lambda1 = [](int a, int b) {
return a + b; // 推导为int
};

auto lambda2 = [](int a, int b) {
if (a > b) return a;
else return b; // 推导为int
};

3.2 指定返回类型

// 显式指定返回类型
auto lambda = [](int a, int b) -> int {
if (a > b) return a;
return b;
};

// 复杂返回类型
auto lambda2 = [](int x) -> std::string {
return "Value: " + std::to_string(x);
};

3.3 返回引用

// 返回引用
int x = 10;
int y = 20;

auto& ref = [&x, &y](bool select) -> int& {
return select ? x : y;
};

ref(true) = 100; // x = 100

四、C++14特性

4.1 泛型Lambda

// 泛型Lambda(使用auto参数)
auto print = [](auto value) {
std::cout << value << std::endl;
};

print(42); // int
print(3.14); // double
print("hello"); // const char*
print(std::string("world")); // std::string

// 多个auto参数
auto add = [](auto a, auto b) {
return a + b;
};

int sum1 = add(1, 2);
double sum2 = add(1.5, 2.5);
std::string sum3 = add(std::string("Hello "), std::string("World"));

4.2 初始化捕获

// C++11:需要在捕获列表中声明
int x = 10;
auto lambda1 = [x] {
return x * 2;
};

// C++14:可以在捕获时初始化
auto lambda2 = [value = 10] {
return value * 2;
};

// 移动语义
auto ptr = std::make_unique<int>(42);
auto lambda3 = [p = std::move(ptr)] {
return *p;
};

// 计算捕获
int multiplier = 2;
auto lambda4 = [result = multiplier * 3] {
return result;
}; // result = 6

// 引用捕获并初始化
int x = 10;
auto lambda5 = [&r = x] {
r = 20;
};
lambda5(); // x = 20

4.3 constexpr Lambda

// C++17:constexpr Lambda
constexpr auto square = [](int x) {
return x * x;
};

// 编译期计算
constexpr int result = square(5); // 25

五、实际应用

5.1 STL算法

#include <algorithm>
#include <vector>

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

// for_each
std::for_each(v.begin(), v.end(), [](int& x) {
x *= 2;
}); // v = {2, 4, 6, 8, 10}

// find_if
auto it = std::find_if(v.begin(), v.end(), [](int x) {
return x > 5;
}); // 找到第一个大于5的元素

// sort
std::sort(v.begin(), v.end(), [](int a, int b) {
return a > b; // 降序排序
});

// accumulate
int sum = std::accumulate(v.begin(), v.end(), 0,
[](int acc, int x) {
return acc + x;
});

5.2 自定义比较

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

std::vector<Person> people = {
{"Alice", 25},
{"Bob", 20},
{"Charlie", 30}
};

// 按年龄排序
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) {
return a.name < b.name;
});

5.3 事件处理

class Button {
public:
using ClickHandler = std::function<void()>;

void setOnClick(ClickHandler handler) {
clickHandler = handler;
}

void click() {
if (clickHandler) {
clickHandler();
}
}

private:
ClickHandler clickHandler;
};

// 使用Lambda处理点击
Button button;
button.setOnClick([]() {
std::cout << "Button clicked!" << std::endl;
});

button.click();

5.4 线程

#include <thread>

// 使用Lambda创建线程
int x = 10;

std::thread t([x]() mutable {
x *= 2;
std::cout << "Thread: " << x << std::endl;
});

t.join();

std::cout << "Main: " << x << std::endl;

六、高级用法

6.1 Lambda作为函数参数

#include <functional>

// 接受Lambda作为参数
void process(std::function<int(int, int)> func) {
int result = func(10, 20);
std::cout << "Result: " << result << std::endl;
}

// 使用
process([](int a, int b) {
return a + b;
});

process([](int a, int b) {
return a * b;
});

6.2 Lambda递归

// 使用std::function实现递归
std::function<int(int)> factorial = [&](int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
};

int result = factorial(5); // 120

6.3 Lambda捕获Lambda

// 捕获另一个Lambda
auto logger = [](const std::string& msg) {
std::cout << "[LOG] " << msg << std::endl;
};

auto process = [logger](const std::string& data) {
logger("Processing: " + data);
// 处理逻辑
logger("Done");
};

process("some data");

6.4 延迟执行

// 延迟执行
auto delayedLog = []() {
std::cout << "This will be executed later" << std::endl;
};

// 保存Lambda
std::vector<std::function<void()>> tasks;

tasks.push_back(delayedLog);

// 执行所有任务
for (auto& task : tasks) {
task();
}

七、性能考虑

7.1 Lambda与函数对象

// Lambda
auto lambda = [](int x) { return x * 2; };

// 等价函数对象
struct Functor {
int operator()(int x) const {
return x * 2;
}
};

// 编译器通常生成相同代码
// 性能相当

7.2 捕获开销

int x = 10;

// 按值捕获:复制开销
auto lambda1 = [x](int y) {
return x + y;
};

// 按引用捕获:无额外开销
auto lambda2 = [&x](int y) {
return x + y;
};

// 建议:大对象按引用捕获
std::vector<int> data(1000);
auto lambda3 = [&data](int index) {
return data[index];
};

7.3 std::function开销

// std::function有额外开销
std::function<int(int)> func = [](int x) { return x * 2; };

// 建议:使用auto避免开销
auto lambda = [](int x) { return x * 2; };

八、最佳实践

8.1 选择捕获方式

// ✅ 小对象按值捕获
int x = 10;
auto lambda1 = [x](int y) {
return x + y;
};

// ✅ 大对象按引用捕获
std::vector<int> data;
auto lambda2 = [&data](int index) {
return data[index];
};

// ✅ 需要修改时按引用捕获
int count = 0;
auto lambda3 = [&count]() {
count++;
};

// ⚠️ 注意悬空引用
// ❌ 错误:返回的Lambda引用了局部变量
auto getLambda() {
int x = 10;
return [&x]() { return x; }; // 危险!
}

// ✅ 正确:按值捕获
auto getLambda2() {
int x = 10;
return [x]() { return x; };
}

8.2 使用auto

// ✅ 推荐:使用auto
auto lambda = [](int x) { return x * 2; };

// ❌ 不推荐:显式类型
std::function<int(int)> lambda2 = [](int x) { return x * 2; };

8.3 简化代码

// ✅ 简洁的Lambda
std::sort(v.begin(), v.end(),
[](int a, int b) { return a > b; });

// ❌ 冗长的命名函数对象
struct Compare {
bool operator()(int a, int b) const {
return a > b;
}
};

std::sort(v.begin(), v.end(), Compare());

九、常见陷阱

9.1 悬空引用

// ❌ 危险:Lambda引用了销毁的变量
std::function<int()> getBadLambda() {
int x = 10;
return [&x] { return x; }; // x已销毁
}

auto lambda = getBadLambda();
// lambda(); // 未定义行为

9.2 捕获this

class MyClass {
int value = 10;

public:
// ❌ 危险:Lambda可能比对象活得更久
std::function<int()> getLambda1() {
return [this] { return value; };
}

// ✅ 安全:捕获副本
std::function<int()> getLambda2() {
return [v = value] { return v; };
}

// ✅ 或者使用shared_from_this
};

9.3 mutable误用

int x = 10;

// ❌ 误用mutable:以为能修改外部变量
auto lambda = [x]() mutable {
x++; // 只修改副本,不影响外部x
};

lambda();
// x仍然是10

// ✅ 正确:使用引用捕获
auto lambda2 = [&x]() {
x++; // 修改外部x
};

十、总结

10.1 核心概念

概念说明
捕获[ ] 控制外部变量访问
参数( ) 定义参数列表
返回类型-> type 或自动推导
mutable允许修改按值捕获的副本
泛型C++14使用auto参数

10.2 选择指南

需要函数对象?

简单且短期使用?
├─ 是 → Lambda
└─ 否 → 命名函数对象

捕获变量?

需要修改外部变量?
├─ 是 → 按引用捕获
└─ 否 → 按值捕获(小对象)/ 按引用捕获(大对象)

10.3 最佳实践

  • ✅ 简单场景使用Lambda
  • ✅ 复杂场景使用命名函数对象
  • ✅ 小对象按值捕获
  • ✅ 大对象按引用捕获
  • ✅ 需要修改时按引用捕获
  • ✅ 使用auto类型
  • ⚠️ 注意悬空引用
  • ⚠️ 注意对象生命周期
  • ❌ 不要过度复杂化