介绍向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:捕获局部变量实现条件筛选
#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:按引用捕获动态更新值
#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:隐式捕获简化代码
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:混合捕获实现灵活访问
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捕获的引用变量在调用前被销毁时,引发未定义行为。
#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
}
解决方案:
#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]):灵活平衡性能与安全性。
- 捕获列表: 按值捕获(
- 核心准则:优先最小化捕获范围,避免隐式捕获(如
[=]或[&]),显式声明更安全。
到此这篇关于c++如何向lambda传递参数与捕获的文章就介绍到这了,更多相关c++ lambda传递参数内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论