C++ Lambda 表达式语法详解

Lambda 表达式的完整语法:

cpp 复制代码
[capture](params) mutable -> return_type { body }

各部分都可以省略,形成不同写法:


1. 最简形式(无捕获、无参数)

cpp 复制代码
auto f = [] { return 42; };
std::cout << f();  // 输出 42

2. 带参数

cpp 复制代码
auto add = [](int a, int b) { return a + b; };
std::cout << add(1, 2);  // 输出 3

3. 显式指定返回类型

当返回类型无法自动推导,或想明确时:

cpp 复制代码
// C++11 风格:尾置返回类型
auto f = [](int x) -> double { return x * 1.5; };

// 常见场景:返回复杂类型或条件分支
auto g = [](bool flag) -> std::string {
    return flag ? "yes" : "no";
};

4. 捕获外部变量(核心用法)

cpp 复制代码
int x = 10;
int y = 20;

// [=] 按值捕获所有外部变量
auto f1 = [=] { return x + y; };

// [&] 按引用捕获所有外部变量
auto f2 = [&] { x = 100; y = 200; };  // 可以修改外部变量
f2();
// 现在 x=100, y=200

// 混合捕获:值捕获 x,引用捕获 y
auto f3 = [=, &y] { y = x + 1; };

// 混合捕获:引用捕获 x,值捕获 y
auto f4 = [&, x] { /* y 可修改,x 只读 */ };

// 只捕获特定变量
auto f5 = [x] { return x; };           // 值捕获 x
auto f6 = [&y] { y = 99; };           // 引用捕获 y
auto f7 = [x, &y] { y = x + 1; };     // 混合

混合捕获详解

[=, &y] --- 默认值捕获,y 单独引用捕获
cpp 复制代码
int x = 10;
int y = 20;
int z = 30;

// [=] 默认按值捕获所有外部变量
// 但 &y 单独指定 y 按引用捕获
auto f = [=, &y] {
    // x、z 是值捕获 → 只读副本,不能修改
    // y 是引用捕获 → 可以修改原变量
    
    // int tmp = x;    // ✅ 可以读 x
    // x = 100;        // ❌ 编译错误:x 是 const 副本
    
    y = x + z;        // ✅ 可以修改 y(引用捕获)
    // z = 50;        // ❌ 编译错误:z 是 const 副本
};

f();
// 执行后:x=10, y=40(被修改了), z=30

记忆规则[=, &y] = "默认全值,y 特殊照顾按引用"

[&, x] --- 默认引用捕获,x 单独值捕获
cpp 复制代码
int x = 10;
int y = 20;
int z = 30;

// [&] 默认按引用捕获所有外部变量
// 但 x 单独指定按值捕获
auto f = [&, x] {
    // y、z 是引用捕获 → 可读可写
    // x 是值捕获 → 只读副本
    
    y = 100;           // ✅ 可以修改 y(引用捕获)
    z = 200;           // ✅ 可以修改 z(引用捕获)
    
    // int tmp = x;    // ✅ 可以读 x
    // x = 50;         // ❌ 编译错误:x 是 const 副本
};

f();
// 执行后:x=10(没变), y=100, z=200

记忆规则[&, x] = "默认全引用,x 特殊照顾按值"


5. mutable 关键字(修改值捕获的副本)

默认情况下,值捕获的变量在 lambda 内是 const 的:

cpp 复制代码
int x = 10;

// 错误:不能修改值捕获的副本
// auto f = [x] { x++; };  // 编译失败

// 正确:加 mutable 允许修改副本(不影响原变量)
auto f = [x]() mutable {
    x++;           // 修改的是副本
    return x;
};

f();  // 返回 11
f();  // 返回 12(副本被保留了)

std::cout << x;  // 原变量仍是 10

6. 初始化捕获(C++14)

在捕获列表里直接初始化:

cpp 复制代码
auto f1 = [x = 10] { return x; };          // 捕获时初始化
auto f2 = [x = std::move(ptr)] { /* ... */ };  // 移动捕获

int a = 5;
auto f3 = [x = a + 1] { return x; };       // 表达式初始化

7. 泛型 lambda(C++14)

参数用 auto,实现类似模板的效果:

cpp 复制代码
// 泛型 lambda,可接受任意类型
auto add = [](auto a, auto b) { return a + b; };

add(1, 2);         // int + int
add(1.5, 2.5);    // double + double
add(std::string("a"), std::string("b"));  // string + string

8. 无捕获 lambda 可转换为函数指针

cpp 复制代码
// 无捕获的 lambda 可以隐式转换为函数指针
int (*fp)(int) = [](int x) { return x * 2; };
std::cout << fp(5);  // 输出 10

// 有捕获的 lambda 不能这样做
int y = 10;
// int (*fp2)(int) = [y](int x) { return x + y; };  // 编译失败

9. 实际场景示例

作为回调函数

cpp 复制代码
std::vector<int> v = {1, 2, 3, 4, 5};

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

// std::find_if 查找
auto it = std::find_if(v.begin(), v.end(), [](int x) {
    return x > 3;
});

作为 std::function 存储

cpp 复制代码
std::function<int(int, int)> f = [](int a, int b) { return a + b; };

用于延迟执行

cpp 复制代码
int threshold = 5;
auto is_large = [threshold](int x) { return x > threshold; };

// 稍后使用
for (int x : {3, 7, 2, 9}) {
    if (is_large(x)) std::cout << x << " ";
}

速查表

写法 含义
[] 不捕获任何外部变量
[=] 按值捕获所有外部变量
[&] 按引用捕获所有外部变量
[x] 只值捕获 x
[&x] 只引用捕获 x
[=, &x] 值捕获所有,但 x 按引用
[&, x] 引用捕获所有,但 x 按值
[x, &y] x 按值,y 按引用
[x = expr] 初始化捕获(C++14)
[...]= 包展开,按值捕获(C++20)
[...&] 包展开,按引用捕获(C++20)
相关推荐
小小码农Come on2 小时前
C++访问QML控件-----QML访问C++对象属性和方法
java·开发语言·c++
Yungoal2 小时前
项目层级结构
c++
程序员-King.3 小时前
【基础分析】—— 条件变量wait(lock, 谓词)
c++·c·多线程·条件变量
故事和你913 小时前
洛谷-算法1-7-搜索3
数据结构·c++·算法·leetcode·动态规划
网域小星球3 小时前
C++ 从 0 入门(四)|继承、多态、this 指针、深浅拷贝(C++ 面试终极收官)
开发语言·c++·面试·多态·继承·this指针·深浅拷贝
想唱rap4 小时前
C++智能指针
linux·jvm·数据结构·c++·mysql·ubuntu·bash
前进吧-程序员5 小时前
现代 C++ 异步编程:从零实现一个高性能 ThreadPool (C++20 深度实践)
开发语言·c++·c++20
pearlthriving5 小时前
c++当中的泛型思想以及c++11部分新特性
java·开发语言·c++
t***5446 小时前
Dev-C++中哪些选项可以设置
开发语言·c++