介绍向Lambda传递参数与捕获列表相关内容
一、向Lambda传递参数
核心规则
Lambda的参数传递机制与普通函数类似,但有以下严格限制:
- 无默认参数:Lambda不支持默认参数,必须显式传递所有参数。
- 参数严格匹配:实参与形参的数量、类型必须完全一致。
示例1:Lambda替代isShorter
函数
// 原比较函数
bool isShorter(const string &a, const string &b) {
return a.size() < b.size();
}
// 用Lambda实现相同功能
vector<string> words = {"apple", "banana", "cherry", "date"};
stable_sort(words.begin(), words.end(),
[](const string &a, const string &b) {
return a.size() < b.size();
});
关键点:
- Lambda形参
a
和b
的类型必须与容器元素类型一致(此处为const string&
)。 stable_sort
每次比较时会自动传递两个元素给Lambda。
二、捕获列表:访问外部变量的桥梁
捕获的本质
Lambda通过捕获列表访问所在函数的局部变量,而非全局变量或静态变量。
- 按值捕获:创建变量的副本,Lambda内部修改不影响外部变量。
- 按引用捕获:直接操作外部变量,需注意变量生命周期。
示例2:捕获局部变量实现条件筛选
cpp
#include <vector>
#include <string>
#include <algorithm>
// 过滤字符串向量,找到第一个长度大于或等于指定大小的字符串
std::vector<std::string>::const_iterator filterWords(const std::vector<std::string> &words, size_t sz) {
// 捕获 sz,按值传递(隐式拷贝),用于查找第一个长度 >= sz 的字符串
auto it = std::find_if(words.begin(), words.end(),
[sz](const std::string &s) {
return s.size() >= sz;
});
return it; // 返回找到的字符串的迭代器
}
int main() {
std::vector<std::string> words = {"apple", "banana", "cherry", "date"};
size_t sz = 6;
auto result = filterWords(words, sz);
if (result != words.end()) {
std::cout << "找到第一个长度 >= " << sz << " 的字符串: " << *result << std::endl;
} else {
std::cout << "未找到长度 >= " << sz << " 的字符串。" << std::endl;
}
return 0;
}
关键点:
sz
是filterWords
函数的局部变量,必须显式捕获才能被Lambda使用。- 此处按值捕获
sz
,Lambda内部使用的是捕获时的副本。
三、捕获方式详解
1. 显式捕获(Explicit Capture)
明确指定要捕获的变量及方式:
- 按值捕获 :
[var]
- 按引用捕获 :
[&var]
示例3:按引用捕获动态更新值
cpp
#include <iostream>
#include <vector>
#include <algorithm>
/**
* @brief 主函数,程序的入口点。
*
* 该函数创建一个整数向量,使用 std::count_if 统计向量中偶数的数量,并输出结果。
*
* @return int 程序的退出状态码,0 表示正常退出。
*/
int main() {
// 定义一个整数向量 nums,并初始化其元素
std::vector<int> nums = {1, 2, 3, 4};
// 定义一个变量 count,用于存储偶数的数量,初始化为 0
int count = 0;
// 使用 std::count_if 统计向量中偶数的数量
// std::count_if 接受三个参数:向量的起始迭代器、结束迭代器和一个谓词函数
// 谓词函数是一个 lambda 表达式,用于判断元素是否为偶数
count = std::count_if(nums.begin(), nums.end(), [&count](int x) {
// 判断元素 x 是否为偶数
return x % 2 == 0;
});
// 输出偶数的数量
std::cout << count << std::endl; // 输出2(统计偶数的数量)
// 返回 0 表示程序正常结束
return 0;
}
关键点:
- 使用
&count
按引用捕获,Lambda内部修改会影响外部变量。 - 若
count
在Lambda调用前被销毁(如局部变量),会导致悬空引用。
2. 隐式捕获(Implicit Capture)
通过[=]
或[&]
批量捕获变量:
[=]
:按值捕获所有外部变量。[&]
:按引用捕获所有外部变量。
示例4:隐式捕获简化代码
cpp
int threshold = 5;
vector<int> data = {3, 7, 2, 8};
// 隐式按值捕获所有外部变量(此处只有threshold)
auto result = count_if(data.begin(), data.end(),
[=](int x) {
return x > threshold;
});
cout << result; // 输出2(7和8大于5)
3. 混合捕获(Mixed Capture)
结合显式和隐式捕获,但需注意优先级:
[=, &var]
:按值捕获所有变量,但var
按引用捕获。[&, var]
:按引用捕获所有变量,但var
按值捕获。
示例5:混合捕获实现灵活访问
cpp
int base = 10;
double factor = 1.5;
vector<int> values = {3, 5, 7};
// 按引用捕获base,按值捕获其他变量
for_each(values.begin(), values.end(),
[&base, factor](int &x) {
x = base + x * factor;
});
// 结果:base=10 → 10+3 * 1.5=14.5 → 转为int为14
// 最终values变为[14, 17, 20]
四、捕获的陷阱与解决方案
陷阱1:悬空引用(Dangling Reference)
当Lambda捕获的引用变量在调用前被销毁时,引发未定义行为。
cpp
#include<iostream>
// 引入输入输出流库,使得程序可以使用 std::cout 进行输出操作
using namespace std;
// 使用标准命名空间,这样可以直接使用标准库中的对象和函数,而无需加 std:: 前缀
auto createLambda() {
// 定义一个函数 createLambda,使用 auto 让编译器自动推导返回类型
int localVar = 42;
// 在函数内部定义一个局部变量 localVar,并初始化为 42
return [&localVar] { return localVar; };
// 返回一个 lambda 表达式,该 lambda 表达式通过值捕获了局部变量 localVar
}
int main() {
// 程序的入口函数
auto lambda = createLambda();
// 调用 createLambda 函数,将返回的 lambda 表达式赋值给变量 lambda
cout << lambda();
// 调用 lambda 表达式,由于 lambda 表达式通过值捕获了 localVar 的副本,所以可以正常输出 42
return 0;
// 程序正常结束,返回 0
}

解决方案:
cpp
#include<iostream>
// 引入输入输出流库,使得程序可以使用 std::cout 进行输出操作
using namespace std;
// 使用标准命名空间,这样可以直接使用标准库中的对象和函数,而无需加 std:: 前缀
auto createLambda() {
// 定义一个函数 createLambda,使用 auto 让编译器自动推导返回类型
int localVar = 42;
// 在函数内部定义一个局部变量 localVar,并初始化为 42
return [localVar] { return localVar; };
// 返回一个 lambda 表达式,该 lambda 表达式通过值捕获了局部变量 localVar
// 值捕获会在创建 lambda 表达式时复制一份 localVar 的副本,即使原变量被销毁,副本仍然有效
}
int main() {
// 程序的入口函数
auto lambda = createLambda();
// 调用 createLambda 函数,将返回的 lambda 表达式赋值给变量 lambda
cout << lambda();
// 调用 lambda 表达式,由于 lambda 表达式通过值捕获了 localVar 的副本,所以可以正常输出 42
return 0;
// 程序正常结束,返回 0
}

陷阱2:修改按值捕获的变量
默认情况下,按值捕获的变量在Lambda内不可修改。
int x = 10;
auto lambda = [x] {
x++; // 编译错误:按值捕获的变量不可修改
};
解决方案 :使用mutable
关键字允许修改副本。
auto lambda = [x]() mutable {
x++; // 修改的是副本,不影响外部x
};
陷阱3:捕获this
指针(类成员访问)
在类成员函数中,Lambda需捕获this
才能访问成员变量。
class Processor {
private:
int offset;
public:
void process(vector<int> &data) {
for_each(data.begin(), data.end(),
[this](int &x) { x += offset; }); // 正确捕获this
}
};
五、捕获与参数传递对比
特性 | 参数传递 | 捕获列表 |
---|---|---|
数据来源 | 调用时传入的实参 | 所在函数的局部变量 |
生命周期 | 由调用者管理 | 按值捕获:与Lambda生命周期一致; 按引用捕获:依赖原变量生命周期 |
修改权限 | 形参可修改(除非声明为const ) |
按值捕获:需mutable ; 按引用捕获:直接修改原变量 |
典型场景 | 比较、转换等需要动态数据的场景 | 条件筛选、状态保持等需要外部变量的场景 |
六、小结
- 参数传递:用于Lambda处理动态输入数据,需严格匹配类型和数量。
- 捕获列表 :
- 按值捕获(
[var]
):适合只读访问或需要副本的场景。 - 按引用捕获(
[&var]
):适合需要修改外部变量或避免拷贝的场景,但需警惕悬空引用。 - 混合捕获(
[=, &var]
):灵活平衡性能与安全性。
- 按值捕获(
- 核心准则 :优先最小化捕获范围,避免隐式捕获(如
[=]
或[&]
),显式声明更安全。