C++ lambda 匿名函数

news/2025/9/17 23:11:13/文章来源:https://www.cnblogs.com/gange111/p/19097864

1、基本介绍

C++11 引入的 lambda 匿名函数(Lambda Expression)是一种轻量级的函数对象,可在需要函数的地方直接定义,无需单独声明,极大简化了代码编写(尤其是回调函数、算法谓词等场景)。

基本语法:

[capture-list] (parameter-list) mutable noexcept(optional) -> return-type { function-body }
组成部分 说明
capture-list 捕获列表:指定如何捕获 lambda 所在作用域的局部变量(值捕获、引用捕获等),不可省略。
parameter-list 参数列表:与普通函数的参数列表一致(可省略,若无形参)。
mutable 可选关键字:允许在 lambda 内部修改值捕获的变量(默认值捕获变量为 const)。
noexcept 可选:指定 lambda 是否可能抛出异常(C++11 起)。
-> return-type 返回类型可选,若函数体仅有一条 return 语句,编译器可自动推导返回类型。
function-body 函数体:lambda 的执行逻辑。

简单的例子

#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};// 使用 Lambda 表达式作为 std::sort 的比较准则// 按降序排序std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; } // Lambda 表达式);for (int i : v) {std::cout << i << " ";}// 输出: 9 6 5 4 3 2 1 1return 0;
}

2、捕获列表

捕获列表定义了 Lambda 表达式如何从其所在的作用域中访问外部变量。

2.1 值捕获

将外部变量的值拷贝到 Lambda 中。Lambda 内部修改不会影响外部变量。

int main() {int x = 10;int y = 20;// 值捕获:将 x 和 y 的当前值拷贝到 Lambda 中auto lambda_val = [x, y]() { std::cout << "Inside lambda (by value): " << x << ", " << y << std::endl;// x++; // 错误!默认情况下,值捕获的变量是 const 的。};x = y = 100; // 修改外部变量lambda_val(); // 调用 Lambda// 输出: Inside lambda (by value): 10, 20return 0;
}

2.2 引用捕获

捕获外部变量的引用。Lambda 内部修改会直接影响外部变量。

int main() {int x = 10;int y = 20;// 引用捕获:捕获 x 和 y 的引用auto lambda_ref = [&x, &y]() { std::cout << "Inside lambda (by ref): " << x << ", " << y << std::endl;x++; y++; // 修改会影响外部变量};lambda_ref(); // 调用 Lambdastd::cout << "After lambda: " << x << ", " << y << std::endl;// 输出: // Inside lambda (by ref): 10, 20// After lambda: 11, 21return 0;
}

2.3 隐式捕获

让编译器根据 Lambda 体内的代码自动推断需要捕获哪些变量。

  • [=]:以值捕获的方式捕获所有使用到的外部变量。
  • [&]:以引用捕获的方式捕获所有使用到的外部变量。
int a = 1, b = 2, c = 3;// 隐式值捕获:自动捕获所有使用到的外部变量 (a, b)
auto lambda1 = [=]() { std::cout << a + b << std::endl; }; 
// 注意:c 没有被使用,所以不会被捕获// 隐式引用捕获:自动捕获所有使用到的外部变量 (a, c)
auto lambda2 = [&]() { std::cout << a + c << std::endl; c = 100; }; 

注意:应谨慎使用隐式捕获,尤其是 [&],因为它可能让你无意中修改外部变量或引入悬空引用。

2.4 混合捕获

int a = 1, b = 2, c = 3, d = 4;// a 显式值捕获,b 显式引用捕获,其他使用到的变量按值捕获(但这里没有其他变量了)
auto lambda1 = [=, &b]() { /* a by value, b by ref */ };// a 显式引用捕获,b 显式值捕获,其他使用到的变量按引用捕获(但这里没有其他变量了)
auto lambda2 = [&, b]() { /* a by ref, b by value */ };// 错误!不能混合相同的捕获模式: [=, a] 或 [&, &b]

2.5 捕获 this 指针

在类的成员函数中,Lambda 可以通过值 [this] 或引用 [&] 捕获 this 指针,从而访问类的成员变量和函数。

class MyClass {
public:void doSomething() {// 捕获 this,从而可以访问成员变量 valueauto lambda = [this]() { std::cout << "Value: " << value << std::endl; memberFunction(); };lambda();}
private:int value = 42;void memberFunction() { std::cout << "Member func called\n"; }
};

2.6 初始化捕获(C++14)

允许在捕获时初始化变量(类似变量声明),解决 “移动捕获” 等场景

#include <vector>
#include <utility>  // for std::moveint main() {vector<int> v = {1, 2, 3};// 初始化捕获:将v移动到lambda内部的vec(避免拷贝大容器)auto func = [vec = move(v)] { cout << "vec size: " << vec.size() << endl; };func();  // 输出:vec size: 3// cout << v.size() << endl;  // 错误:v已被移动,处于无效状态return 0;
}

3、mutable 关键字

默认情况下,对于值捕获的变量,Lambda 的 operator()const 的,这意味着你不能在 Lambda 体内修改这些拷贝。
使用 mutable 关键字可以移除这个 const 限制。

int main() {int count = 0;// 没有 mutable: 错误!不能修改值捕获的变量。// auto lambda = [count]() { count++; }; // 使用 mutableauto lambda = [count]() mutable { count++; // 现在可以修改了std::cout << "Count inside lambda: " << count << std::endl;};lambda(); // 输出: Count inside lambda: 1lambda(); // 输出: Count inside lambda: 2std::cout << "Count outside: " << count << std::endl; // 输出: Count outside: 0// 注意:修改的是 Lambda 内部的副本,不影响外部变量。return 0;
}

重要mutable 允许你修改的是 Lambda 内部副本的值,对外部变量毫无影响。引用捕获不需要 mutable

4、返回类型

编译器通常可以自动推导 Lambda 的返回类型。但如果函数体中有多个返回语句且类型不同,或者你想要更明确的代码,可以显式指定。

std::vector<int> numbers = {1, 2, 3, 4, 5};// 编译器自动推导返回类型为 bool
auto is_even = [](int n) { return n % 2 == 0; };// 显式指定返回类型为 double (使用尾置返回类型语法)
auto divide = [](int a, int b) -> double {if (b == 0) {return 0.0; // 返回 double}return static_cast<double>(a) / b; // 返回 double
};

5、常见用法与示例

5.1 与 STL 算法结合

std::vector<int> vec = {5, 3, 8, 1, 9};
// 计算大于 5 的元素数量
int count = std::count_if(vec.begin(), vec.end(), [](int n) { return n > 5; });
// 将所有元素翻倍
std::for_each(vec.begin(), vec.end(), [](int& n) { n *= 2; }); // 注意:需要引用才能修改原值vector<int> nums = {3, 1, 4, 1, 5, 9};// 用lambda作为sort的比较函数(降序排序)
sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; 
});  // nums变为:9,5,4,3,1,1// 用lambda作为find_if的条件(查找偶数)
auto it = find_if(nums.begin(), nums.end(), [](int x) { return x % 2 == 0; 
});
if (it != nums.end()) {cout << "找到偶数:" << *it << endl;  // 输出:4
}

5.2 并发编程中的任务

线程或异步任务(std::threadstd::async)需要执行函数,lambda 可直接定义任务逻辑

#include <thread>
#include <future>int main() {// 线程任务用lambda定义thread t([] { cout << "线程执行中..." << endl; });t.join();// 异步任务用lambda定义future<int> fut = async([] { return 1 + 2; });cout << "异步结果:" << fut.get() << endl;  // 输出:3return 0;
}

5.3 自定义比较器

std::map<std::string, int> name_age;
// 按值(年龄)排序,而不是键(名字)
std::vector<std::pair<std::string, int>> vec(name_age.begin(), name_age.end());
std::sort(vec.begin(), vec.end(),[](const auto& a, const auto& b) { return a.second < b.second; });

6、常见问题

1. Lambda 表达式中的捕获列表 [=][&] 有什么区别?
[=] 表示隐式值捕获,Lambda 体内使用的所有外部变量都会将其当前值拷贝一份到 Lambda 对象中。[&] 表示隐式引用捕获,Lambda 体内使用的所有外部变量都会以其引用被捕获,在 Lambda 内部修改它们会影响外部变量。应谨慎使用 [&],以免造成意外的副作用或悬空引用。

2. 什么是“初始化捕获”(Init Capture)?它解决什么问题?
初始化捕获(C++14)允许在捕获列表中直接初始化一个新的成员变量。它主要解决了移动捕获的问题。例如,你不能用普通捕获移动一个 std::unique_ptr(因为无法拷贝),但可以用 [p = std::move(unique_ptr)] 将其所有权移动到 Lambda 内部。它也允许你以任意表达式初始化捕获的变量。

3. mutable 关键字在 Lambda 中起什么作用?
默认情况下,对于值捕获的变量,Lambda 的函数调用运算符 (operator()) 是 const 的,这意味着你不能修改这些捕获的副本。mutable 关键字移除了这个 const 限制,允许你修改 Lambda 内部的值捕获变量。需要注意的是,这修改的只是副本,不影响外部原始变量。

4. Lambda 表达式的类型是什么?如何存储或传递一个 Lambda?
每个 Lambda 表达式都会生成一个唯一的、编译器生成的、未命名的类型(闭包类型)。存储和传递它的最佳方式是:

  1. 使用 auto 进行初始化(auto lambda = [...](){...};)。
  2. 使用 std::function(如 std::function<void()>),这会带来一些类型擦除的开销,但非常灵活。
  3. 在模板中使用(template<typename F> void foo(F func)),这是零开销的方式。

5. 在类的成员函数中,Lambda 如何访问类的成员变量?
需要通过捕获 this 指针。使用 [this][&](隐式捕获)可以捕获当前对象的 this 指针,从而在 Lambda 内部访问类的成员变量和成员函数。需要注意的是,如果 Lambda 的生命周期可能比对象更长(例如,被放入一个全局队列),这会导致悬空 this 指针。C++17 的 [\*this] 可以按值捕获整个对象的副本,避免这个问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/906929.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

D拼数

include<stdio.h> include<stdlib.h> #include<string.h> int compare(const void *a, const void *b) { char str1[40], str2[40]; // 1:a在前,b在后 sprintf(str1, "%d%d", *(int *)a…

20250917 - WETToken 攻击事件:价格操控产生的套利空间

背景信息Alert:https://x.com/TenArmorAlert/status/1968223320693686423 TX:https://app.blocksec.com/explorer/tx/bsc/0xf92539acf7eadfd4a98925927a52af5349cb13c2a250908373a5baf8ea4b49adTrace 分析 发生攻击的…

US$18 3 Button Smart Card For Hyundai 433.92MHz

3 Button Smart Card For Hyundai 433.92MHz Package includes:1pc x 3 Button Smart Card For Hyundai 433.92MHz Pictures of 3 Button Smart Card For Hyundai 433.92MHz3 Button Smart Card For Hyundai 433.92MHz…

题解:P6798 「StOI-2」简单的树

简单的树: 题意: 一颗树,每个节点有一个权值 \(c_i\)。 \(val_i\):\(i\) 为根的子树内所有 \(c_i\) 的最大值。 \(f(x,y)\):\(c_{x}\) 改为 \(y\) 后 \(val_i\) 之和。 每次询问给定 \((l,r,a)\) ,求 \(\sum\lim…

题解:P11704 [ROIR 2025] 旅行路线

旅行路线: 很有参考价值的一道题,其他题解有点抽象,我来。 转化题意 题意转化为 \((1,2)→(n-1,m),(2,1)→(n,m-1)\) 的两条链不相交且经过所有关键点的方案数。 其他点没用,我们以下的点指关键点。 无不能相交限制…

题解:P11292 【MX-S6-T4】「KDOI-11」彩灯晚会

彩灯晚会:\(n\) 点 \(m\) 边 \(k\) 种颜色,给每个点染色。 \(cnt_i\):第 \(i\) 种颜色长度为 \(l\) 的链的数量。其中 \(l\) 为题目给的一个常量。 求 \(\sum_{染色方案}\sum_{i=1}^k cnt_i^2\) 的和。一\(\sum_{染…

算法课程第一周作业

《数学之美》第一章启示 《数学之美》的第一章,在算法工程师眼中,并非传授某个具体算法.而是重构了我们理解、设计和应用算法的底层思维框架,世界的基本问题是算法问题,而数学是寻找最优算法的终极语言。 启示一:所有问…

US$7.9 3 Button Remote Key for Chrysler/Jeep 315Mhz

3 Button Remote Key for Chrysler/Jeep 315Mhz GQ43VT13T Package includes:1pc x 3 Button Remote Key for Chrysler/Jeep 315Mhz GQ43VT13T Pictures of 3 Button Remote Key for Chrysler/Jeep 315Mhz3 Button Rem…

vue3 内存溢出临时方法

"serve1":"node --max_old_space_size=8096 node_modules/@vue/cli-service/bin/vue-cli-service.js serve --open","serve2":"node --max_old_space_size=9096 node_modules/@vue…

实测对比:权威榜单之微信排版Top 5编辑器大揭秘

在新媒体运营的世界里,微信排版可是重中之重,它直接影响着文章的视觉效果和读者的阅读体验。很多运营人都有这样的痛点:写作慢、排版耗时、跨平台排版不统一、配图难还可能有侵权风险等。为了帮大家解决这些难题,我…

自建仓库推送到NAS采用 Docker Registry 工作流

放弃手动 `save` 和 `load` 的方式,改用行业标准的 Registry(仓库)模式。这是最专业、最高效的方案。 **优点**: - **彻底解决版本兼容性问题**,因为 push/pull 协议是标准化的。 - 传输效率高,再次推送时只会上…

【汇编和指令集 . 第2025 . 9期】发现大牛

【编者按】在计算机、互联网风行半个世纪之后,我们发现:科技预言家越来越多了,思想家缺位了。生活节奏变快了,思想退步了;书写减少了,纸张缺没少;知识泛滥了,思考没有深入......我们有可能遭AI时代的反噬。时代…

Opencompass避坑日记

安装首先执行pip安装 再下载源代码第一句是为了安装opencompass的依赖包,第二句是为了在当前目录引入本地目录的opencomass模块。 因为有很多修改的地方。 测评VLLM 放弃吧,这个框架对VLLM的支持很差。测评方式:稳定…

随笔 | 农场、小猴子、香蕉

在一个偏西部的农场中,有着一群猴子,他们每天的任务,是将香蕉树上的香蕉摘下来,而他们的报酬是仅仅九根香蕉,每天早上四根,每天晚上五根。某一天,其中一只猴子报怨,每天早上只能吃到四根香蕉,他提议说,改成每…

Day17数组的使用

package com.cc.array;public class ArrayDemo4 {public static void main(String[] args) {int [] arrays = {1,2,3,4,5};//jdk1.5之后的版本可以通果增强for寻循环来遍历数组或集合中的每一个元素//缺点在于没有下标…

US$17 4+1 Button Smart Card for chrysler/Dodge

4+1 Button smart card for chrysler/Dodge Package includes:1pc x 4+1 Button smart card for chrysler/Dodge Pictures of 4+1 Button Smart Card for chrysler/Dodge4+1 Button Smart Card for chrysler/Dodge, no…

完整教程:缓存与数据库一致性的4大坑及终极解决方案

完整教程:缓存与数据库一致性的4大坑及终极解决方案pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&quo…

Rust的Cargo用法详解 - 详解

Rust的Cargo用法详解 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco",…

串行通信接口标准(TTL、CMOS、RS232、RS422、RS485、CAN等)

TTL电平 引言 TTL是 Transistor-Transistor Logic(晶体管-晶体管逻辑)的缩写,是早期基于双极性晶体管(BJT)技术的逻辑家族。 电平特点 1. 电源电压:+5V 2. 电平标准:Voh:≥ 2.4V; Vol: ≤ 0.4V; Vih:≥ 2.0…

攻防世界-IgniteMe - xxx

先查壳,发现没加壳,拖入ida-32反汇编了得到主函数 粗略看一下,能得到的信息有 输入的字符串长度为29,前四个字符是EIS{,最后一个字符是}想要输出Congratulations!关键的函数就是这个 4011C0函数,我们点进去看一下…