《C++11》Lambda 匿名函数从入门到进阶 优缺点分析 示例
Lambda 匿名函数从入门到进阶
C++11 引入了 lambda 表达式,这是一种非常强大的功能,可以让我们在代码中定义匿名函数。它们不仅使代码更加简洁,而且在处理回调、算法和多线程编程时极为方便。本文将带你从入门到进阶,全面了解 C++11 中的 lambda 表达式。
什么是 Lambda 表达式?
Lambda 表达式是一种可以在运行时定义的匿名函数。它们通常用于需要函数作为参数的场景,比如 STL 算法。Lambda 表达式的基本语法如下:
[capture](parameters) -> return_type {// function body
}
1. 基本语法解析
- capture:捕获外部变量的方式,可以是值捕获或引用捕获。
- parameters:函数参数列表。
- return_type:返回类型,可以省略,编译器会自动推导。
- function body:函数体,包含具体的实现逻辑。
2. 简单示例
让我们从一个简单的例子开始,看看如何使用 lambda 表达式。
#include <iostream>int main() {auto greet = []() {std::cout << "Hello, Lambda!" << std::endl;};greet(); // 调用 lambda 函数return 0;
}
在这个例子中,我们定义了一个简单的 lambda 表达式 greet
,它没有参数,直接输出一条信息。
3. 捕获外部变量
Lambda 表达式的一个强大之处在于它可以捕获外部变量。我们可以通过值或引用来捕获这些变量。
3.1 值捕获
#include <iostream>int main() {int x = 10;auto add_x = [x](int y) {return x + y;};std::cout << "Result: " << add_x(5) << std::endl; // 输出 15return 0;
}
在这个例子中,x
被值捕获,lambda 表达式 add_x
可以使用 x
的值。
3.2 引用捕获
#include <iostream>int main() {int x = 10;auto add_ref = [&x](int y) {return x + y;};x = 20; // 修改 x 的值std::cout << "Result: " << add_ref(5) << std::endl; // 输出 25return 0;
}
这里,x
被引用捕获,因此在 lambda 表达式中使用的是 x
的引用,任何对 x
的修改都会影响到 lambda 表达式的结果。
4. 参数和返回类型
Lambda 表达式可以接受参数,并且可以指定返回类型。
#include <iostream>int main() {auto multiply = [](int a, int b) -> int {return a * b;};std::cout << "Result: " << multiply(3, 4) << std::endl; // 输出 12return 0;
}
在这个例子中,multiply
是一个接受两个整数参数并返回它们乘积的 lambda 表达式。
5. 使用 Lambda 表达式与 STL 算法
Lambda 表达式在 STL 算法中非常有用,能够让我们以更简洁的方式实现自定义的比较或操作。
#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5};// 使用 lambda 表达式进行排序std::sort(numbers.begin(), numbers.end(), [](int a, int b) {return a > b; // 降序排序});std::cout << "Sorted numbers: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;return 0;
}
在这个例子中,我们使用 lambda 表达式对 numbers
向量进行降序排序。
6. 进阶用法:捕获所有变量
如果你想捕获所有外部变量,可以使用 [&]
或 [=]
。
[&]
:引用捕获所有外部变量。[=]
:值捕获所有外部变量。
#include <iostream>int main() {int a = 5, b = 10;auto sum = [&]() {return a + b; // 引用捕获};std::cout << "Sum: " << sum() << std::endl; // 输出 15return 0;
}
7. Lambda 表达式的局限性
尽管 lambda 表达式非常强大,但它们也有一些局限性:
- 不能定义递归:由于没有名称,lambda 表达式不能直接递归调用自己。
- 捕获限制:捕获的变量必须在 lambda 表达式的作用域内有效。
8. 实际应用示例
让我们看一个更复杂的示例,结合 lambda 表达式和 STL 容器,计算一个向量中所有偶数的平方和。
#include <iostream>
#include <vector>
#include <numeric>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6};int sum_of_squares = std::accumulate(numbers.begin(), numbers.end(), 0,[](int sum, int n) {return n % 2 == 0 ? sum + n * n : sum; // 只计算偶数的平方});std::cout << "Sum of squares of even numbers: " << sum_of_squares << std::endl; // 输出 56return 0;
}
在这个例子中,我们使用 std::accumulate
和 lambda 表达式来计算偶数的平方和。
小结
C++11 的 lambda 表达式为我们提供了一种灵活且强大的方式来定义匿名函数。通过捕获外部变量、接受参数和返回值,lambda 表达式可以极大地简化代码并提高可读性。希望本文能帮助你从入门到进阶,掌握 C++11 中的 lambda 表达式!如果你有任何问题或想法,欢迎在评论区分享!
Lambda 表达式的优缺点分析及示例
C++11 引入的 lambda 表达式为编程提供了更高的灵活性和简洁性,但它们也有一些局限性。本文将分析 lambda 表达式的优缺点,并通过示例来说明它们的应用。
优点
1. 简洁性
Lambda 表达式允许我们在需要函数的地方直接定义函数体,减少了代码的冗余。例如,在使用 STL 算法时,可以直接在调用时定义操作,而不需要单独定义一个函数。
示例:
#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5};// 使用 lambda 表达式进行平方操作std::for_each(numbers.begin(), numbers.end(), [](int &n) {n = n * n;});std::cout << "Squared numbers: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;return 0;
}
在这个例子中,我们使用 lambda 表达式直接在 std::for_each
中定义了对每个元素的操作。
2. 捕获外部变量
Lambda 表达式可以捕获外部作用域中的变量,这使得它们在处理回调和异步操作时非常方便。
示例:
#include <iostream>int main() {int x = 10;auto add_x = [x](int y) {return x + y; // 捕获外部变量 x};std::cout << "Result: " << add_x(5) << std::endl; // 输出 15return 0;
}
在这个例子中,lambda 表达式 add_x
捕获了外部变量 x
,使得它可以在函数体内使用。
3. 代码可读性
使用 lambda 表达式可以使代码更具可读性,尤其是在处理复杂的操作时。它们可以将操作逻辑与调用逻辑紧密结合,减少上下文切换。
示例:
#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5};// 计算偶数的和int sum = std::accumulate(numbers.begin(), numbers.end(), 0,[](int total, int n) {return n % 2 == 0 ? total + n : total;});std::cout << "Sum of even numbers: " << sum << std::endl; // 输出 6return 0;
}
在这个例子中,lambda 表达式使得计算偶数和的逻辑非常清晰。
4. 支持类型推导
Lambda 表达式的参数和返回类型可以由编译器自动推导,这减少了类型声明的负担。
示例:
#include <iostream>int main() {auto multiply = [](auto a, auto b) {return a * b; // 使用 auto 进行类型推导};std::cout << "Result: " << multiply(3, 4) << std::endl; // 输出 12std::cout << "Result: " << multiply(3.5, 2.0) << std::endl; // 输出 7return 0;
}
在这个例子中,lambda 表达式 multiply
可以接受不同类型的参数,编译器会根据传入的参数类型自动推导。
缺点
1. 不能递归调用
由于 lambda 表达式没有名称,因此不能直接在其内部递归调用自己。如果需要递归,必须使用 std::function
或其他方法来实现。
示例:
#include <iostream>
#include <functional>int main() {std::function<int(int)> factorial = [&](int n) {return n <= 1 ? 1 : n * factorial(n - 1); // 使用 std::function 实现递归};std::cout << "Factorial of 5: " << factorial(5) << std::endl; // 输出 120return 0;
}
在这个例子中,我们使用 std::function
来实现递归调用。
2. 性能开销
虽然 lambda 表达式在许多情况下可以提高代码的可读性,但它们可能会引入一些性能开销,尤其是在捕获大量变量时。捕获的变量会被复制到 lambda 表达式的上下文中。
示例:
#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> large_vector(1000000, 1);int total = 0;// 使用 lambda 表达式计算总和auto sum_lambda = [&total](int n) {total += n; // 捕获 total};std::for_each(large_vector.begin(), large_vector.end(), sum_lambda);std::cout << "Total: " << total << std::endl; // 输出 1000000return 0;
}
在这个例子中,虽然 lambda 表达式简化了代码,但如果 large_vector
中的元素非常多,可能会导致性能下降。
3. 复杂性增加
在某些情况下,过度使用 lambda 表达式可能会导致代码变得难以理解,尤其是当 lambda 表达式嵌套或捕获多个变量时。
示例:
#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5};// 复杂的 lambda 表达式auto complex_lambda = [](const std::vector<int>& nums) {return std::accumulate(nums.begin(), nums.end(), 0,[](int total, int n) {return n % 2 == 0 ? total + n * n : total; // 嵌套 lambda});};std::cout << "Complex sum: " << complex_lambda(numbers) << std::endl; // 输出 20return 0;
}
在这个例子中,嵌套的 lambda 表达式可能会让代码的可读性降低。
总结
C++ 的 lambda 表达式为我们提供了强大的功能,能够简化代码、提高可读性,并支持捕获外部变量。然而,它们也有一些局限性,如不能递归调用、可能引入性能开销以及在复杂情况下可能导致可读性下降。在使用 lambda 表达式时,开发者需要权衡这些优缺点,以便在合适的场景中充分利用它们的优势。
相关文章:

《C++11》Lambda 匿名函数从入门到进阶 优缺点分析 示例
Lambda 匿名函数从入门到进阶 C11 引入了 lambda 表达式,这是一种非常强大的功能,可以让我们在代码中定义匿名函数。它们不仅使代码更加简洁,而且在处理回调、算法和多线程编程时极为方便。本文将带你从入门到进阶,全面了解 C11 …...

连接Milvus
连接到Milvus 验证Milvus服务器正在侦听哪个本地端口。将容器名称替换为您自己的名称。 docker port milvus-standalone 19530/tcp docker port milvus-standalone 2379/tcp docker port milvus-standalone 192.168.1.242:9091/api/v1/health 使用浏览器访问连接地址htt…...
Linux——修改文件夹的所属用户组和用户
一、命令 举例: 授权 MOT17 文件夹 给 hust_xxx 用户: sudo chown -R hust_xxx:hust_xxx MOT17参考 Linux授权文件夹给用户...

Vue Amazing UI 组件库(Vue3+TypeScript+Vite 等最新技术栈开发)
Vue Amazing UI 一个 Vue 3 组件库 使用 TypeScript,都是单文件组件 (SFC),支持 tree shaking 有点意思 English | 中文 Vue Amazing UI 是一个基于 Vue 3、TypeScript、Vite 等最新技术栈开发构建的现代化组件库,包含丰富的 UI 组件和常…...

计算机Steam报错failedtoloadsteamui.dll怎么解决?DLL报错要怎么修复?
计算机Steam报错“Failed to Load SteamUI.dll”?这里有专业的解决方案! 作为软件开发领域的一名从业者,我深知电脑在运行过程中可能会遇到的各种问题,尤其是像Steam这样的大型游戏平台。今天,我将为大家科普一下Stea…...

如何开发一个简单的 dApp
后端合约 执行 sui move new resource_manage 创建一个包 接着就可以开始编写合约了 首先创建两个 Struct 用来创建 Profile 并记录在 State 中 public struct State has key {id: UID,users: Table<address, address>, }public struct Profile has key {id: UID,nam…...
TDengine 签约智园数字,助力化工园区智联未来
近年来,随着化工行业对安全、环保、高效运营的要求日益提高,化工园区的数字化转型成为必然趋势。从数据孤岛到全面互联,从基础监控到智能分析,如何高效管理和利用时序数据已成为化工园区智能化升级的关键环节。作为一家专注于时序…...

《Python游戏编程入门》注-第9章8
2 游戏信息的显示 在游戏窗口的上部会显示游戏分数、游戏关卡、剩余砖块数以及剩余小球数等信息,如图12所示。 图12 游戏信息显示 使用如图13所示的代码实现以上功能。 图13 显示游戏信息的代码 其中,print_text()函数MyLibrary....

js逆向实战(1)-- 某☁️音乐下载
下载某云音乐源文件.mp4格式 首先随便点进一首歌,如图所示获取该音乐id,然后点击播放键,打开F12进行查询XHR 由此可知,实际请求网址是 https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token「你的token」url需带…...

AIA - APLIC之三(附APLIC处理流程图)
本文属于《 RISC-V指令集基础系列教程》之一,欢迎查看其它文章。 1 APLIC复位 APLIC复位后,其所有状态都变得有效且一致,但以下情况除外: 每个中断域的domaincfg寄存器(spec第 4.5.1 节);可能是machine-level interrupt domain的MSI地址配置寄存器(spec第4.5.3 和4.5…...

React Router 向路由组件传state参数浏览器回退历史页面显示效果问题
昨天在看尚硅谷张天禹老师讲的 React教程p90,老师讲到 React路由的 replace模式和push模式,老师的演示效果与自己本地操作不太一样。 老师的效果:点击查看消息1,消息2,消息3 再点回退,可以依次查看到 消息…...
线程池与并发工具:Java的分身管理器
1 线程池的概念 线程池是一种执行器(Executor),用于在一个后台线程中执行任务。线程池的主要目的是减少在创建和销毁线程时所产生的性能开销。通过重用已经创建的线程来执行新的任务,线程池提高了程序的响应速度,并且提…...
字玩FontPlayer开发笔记8 Tauri2文件系统
字玩FontPlayer开发笔记8 Tauri2文件系统 字玩FontPlayer是笔者开源的一款字体设计工具,使用Vue3 ElementUI开发,源代码: github: https://github.com/HiToysMaker/fontplayer gitee: https://gitee.com/toysmaker/fontplayer 笔记 字玩目…...
头歌python实验:网络安全应用实践3-验证码识别
第1关:简单的验证码识别 本关任务:编写一个能简单识别验证码的小程序。 为了完成本关任务,你需要掌握: 使用 pytesseract 库与 PIL 库解析图片;环境配置;读取图片文本信息。使用 pytesseract 库与 PIL 库解析图片 pytesseract 库可以从图像中提取文本。Tesseract 是一…...

客户案例:基于慧集通(DataLinkX)集成平台的金蝶云星空与HIS系统集成案例--凭证模板的配置(一)
当前的原型客户是一家医院,财务系统使用的是金蝶云星空,需要与医院专用的HIS系统进行集成。本文档主要是介绍其中的凭证模板的配置功能。 凭证模板组件旨在生成凭证前,通过内部整理整合原始单据数据,将其转化为可生成一张凭证的数…...
基于 Python 的大学教室资源管理系统的设计与实现
标题:基于 Python 的大学教室资源管理系统的设计与实现 内容:1.摘要 摘要:随着高校教育的不断发展,教室资源的管理变得越来越重要。为了提高教室资源的利用率,本文设计并实现了一个基于 Python 的大学教室资源管理系统。该系统采用了 B/S 架…...

nginx-灰度发布策略(split_clients)
一. 简述: 基于客户端的灰度发布(也称为蓝绿部署或金丝雀发布)是一种逐步将新版本的服务或应用暴露给部分用户,以确保在出现问题时可以快速回滚并最小化影响的技术。对于 Nginx,可以通过配置和使用不同的模块来实现基于…...

nginx正向代理从安装到使用一网打尽系列(二)使用
一、背景 使用场景大总结,可作为参考手册用 nginx正向代理从安装到使用一网打尽系列(一)安装 nginx正向代理从安装到使用一网打尽系列(二)使用 二、使用场景 1、所有内网应用都不能直接访问外网,但需要…...

Bash Shell的操作环境
目录 1、路径与指令搜寻顺序 2、bash的进站(开机)与欢迎信息:/etc/issue,/etc/motd (1)/etc/issue (2)/etc/motd 3、bash的环境配置文件 (1)login与non-…...

Python爬虫基础——认识网页结构(各种标签的使用)
1、添加<div>标签的代码定义了两个区块的宽度和高度均为100px,边框的格式也相同,只是区块中显示的内容不同; 2、添加<ul>和<ol>标签分别用于定义无序列表和有序列表。<il>标签位于<ul>标签或<ol>标签之…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...