C++:向Lambda传递参数与捕获

介绍向Lambda传递参数与捕获列表相关内容


一、向Lambda传递参数

核心规则

Lambda的参数传递机制与普通函数类似,但有以下严格限制:

  1. 无默认参数:Lambda不支持默认参数,必须显式传递所有参数。
  2. 参数严格匹配:实参与形参的数量、类型必须完全一致。
示例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形参ab的类型必须与容器元素类型一致(此处为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;
}

关键点

  • szfilterWords函数的局部变量,必须显式捕获才能被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]):灵活平衡性能与安全性。
  • 核心准则 :优先最小化捕获范围,避免隐式捕获(如[=][&]),显式声明更安全。
相关推荐
小张-森林人40 分钟前
C#中,什么是委托,什么是事件及它们之间的关系
开发语言·c#
何似在人间57542 分钟前
JAVA实现动态IP黑名单过滤
java·开发语言·tcp/ip·nacos·黑名单过滤
demonlg01121 小时前
Go 语言标准库中log模块详细功能介绍与示例
开发语言·后端·云原生·golang
努力学习的小廉1 小时前
【C++】 —— 笔试刷题day_9
开发语言·c++·代理模式
Yhame.1 小时前
【 C 语言实现顺序表的基本操作】(数据结构)
c语言·开发语言·数据结构
m0_687399841 小时前
C++ QT defined a CustomTreeView, 可选择多个node,并且可拖动这些node,改变父节点
java·c++·qt
eqwaak01 小时前
京东商品爬虫技术解析:基于Selenium的自动化数据采集实战
开发语言·人工智能·爬虫·python·selenium·自动化
UestcXiye1 小时前
《TCP/IP网络编程》学习笔记 | Chapter 22:重叠 I/O 模型
c++·计算机网络·网络编程·ip·tcp
muzi_liii1 小时前
封装红黑树模拟实现map和set
c++
速盾cdn2 小时前
速盾:Python可以用高防CDN吗?
开发语言·网络·python