【C++11】lambda表达式

前言:

随着 C++11 的发布,C++ 标准引入了许多新特性,使语言更加现代化,开发者编写的代码也变得更加简洁和易于维护。Lambda 表达式是其中一个重要的特性,它提供了一种方便的方式来定义匿名函数,这在函数式编程范式中非常常见。Lambda 表达式允许我们在函数内部定义小型的无名函数,减少了不必要的函数定义和代码冗余,是现代 C++ 开发的重要工具。

本文将详细介绍 C++11 中 Lambda 表达式的语法、使用场景、捕获机制以及高级应用,帮助读者充分理解并应用这个特性。


一、Lambda 表达式的基础

Lambda 表达式的基本语法如下:

[capture] (parameters) -> return_type { body }
  • capture(捕获列表):定义了哪些外部变量会被“捕获”并在 lambda 表达式内部使用。它可以按值或按引用捕获。
  • parameters(参数列表):与普通函数的参数列表相似,定义了传递给 lambda 的参数。
  • return_type(返回类型):指定返回类型,C++11 可以自动推导返回类型,因此该部分可以省略。
  • body(函数体):lambda 实际执行的代码块。

一个简单的 Lambda 表达式示例:

auto add = [](int a, int b) -> int { return a + b; };
std::cout << add(5, 3) << std::endl;  // 输出 8

这里我们定义了一个接受两个 int 参数并返回它们之和的匿名函数。lambda 通过 [] 定义了捕获列表,(int a, int b) 是参数列表,-> int 是返回类型,而函数体 { return a + b; } 执行了实际的操作。


二、捕获列表

捕获列表定义了 lambda 可以使用的外部变量。捕获方式分为两种:按值捕获和按引用捕获。通过不同的捕获方式,可以控制 lambda 对外部变量的访问和修改行为。

1. 不捕获任何变量

如果 lambda 不需要使用任何外部变量,可以使用空捕获列表:

auto sayHello = []() { std::cout << "Hello, World!" << std::endl; };
sayHello();  // 输出:Hello, World!

2. 按值捕获(=

按值捕获意味着 lambda 内部得到的是捕获变量的副本,因此在 lambda 内对该变量的修改不会影响外部变量。

int x = 10;
auto captureByValue = [x]() { std::cout << x << std::endl; };
x = 20;
captureByValue();  // 输出 10

在上面的例子中,虽然我们在 lambda 定义之后修改了 x 的值,但 lambda 捕获的是 x 的副本,因此输出的仍然是 10。

3. 按引用捕获(&

按引用捕获则允许 lambda 直接访问外部变量并修改它的值。

int x = 10;
auto captureByReference = [&x]() { x += 10; };
captureByReference();
std::cout << x << std::endl;  // 输出 20

这里我们通过 &xx 按引用捕获,因此在 lambda 中对 x 的修改会直接影响外部变量。

4. 捕获特定变量

可以只捕获特定的变量,而不是捕获所有外部变量。捕获方式可以是按值或按引用:

int a = 5, b = 10;
auto captureSpecific = [a, &b]() {std::cout << "a: " << a << std::endl;std::cout << "b: " << b << std::endl;b = 20;
};
captureSpecific();
std::cout << "b: " << b << std::endl;  // 输出 20

在这个例子中,a 被按值捕获,b 被按引用捕获。lambda 内部修改了 b,而外部的 b 也受到了影响。

5. 捕获所有变量([=][&]

  • [=]:按值捕获所有外部变量。
  • [&]:按引用捕获所有外部变量。
int x = 5, y = 10;
auto captureAllByValue = [=]() {std::cout << "x: " << x << ", y: " << y << std::endl;
};
captureAllByValue();  // 输出:x: 5, y: 10auto captureAllByReference = [&]() {x += 10;y += 10;
};
captureAllByReference();
std::cout << "x: " << x << ", y: " << y << std::endl;  // 输出:x: 15, y: 20

通过 [=],lambda 按值捕获了 xy,因此即使在外部修改了 xy,lambda 内的 xy 仍然保持不变。而通过 [&],lambda 可以直接修改外部的 xy


三、Lambda 表达式的返回类型

在 C++11 中,lambda 的返回类型可以自动推导,也可以显式指定。通常,如果 lambda 体中只有一个 return 语句,编译器会自动推导返回类型:

auto add = [](int a, int b) { return a + b; };
std::cout << add(3, 4) << std::endl;  // 输出 7

如果 lambda 体中有复杂的逻辑或多个 return 语句,建议使用 -> 明确指定返回类型:

auto complexLambda = [](int a, int b) -> int {if (a > b) return a;else return b;
};
std::cout << complexLambda(3, 4) << std::endl;  // 输出 4

自动推导有时会导致问题,特别是当 lambda 返回一个引用或指针时,明确返回类型显得尤为重要。


四、Lambda 的常见使用场景

1. 用于标准算法

C++ 标准库中提供了许多算法,如 std::sortstd::for_each 等,lambda 可以很方便地作为这些算法的回调函数使用。

std::vector<int> v = {1, 2, 3, 4, 5};
std::for_each(v.begin(), v.end(), [](int &n) { n *= 2; });
for (auto n : v) std::cout << n << " ";  // 输出 2 4 6 8 10

在这个例子中,lambda 作为 std::for_each 的参数,将每个元素乘以 2。

2. 用于排序

使用 lambda 作为自定义排序条件,可以让排序更加灵活。

std::vector<int> v = {5, 2, 9, 1, 5, 6};
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
for (auto n : v) std::cout << n << " ";  // 输出 9 6 5 5 2 1

这里,我们通过 lambda 定义了一个降序排序的条件。

3. 用于事件回调

在 GUI 或网络编程中,lambda 常用于定义事件的回调函数。例如,在按钮点击事件中:

button.onClick([]() {std::cout << "Button clicked!" << std::endl;
});

lambda 作为回调函数的使用使得代码更加简洁。


五、Lambda 表达式的高级用法

1. 捕获 this 指针

在类成员函数中,lambda 可以通过 [this] 捕获当前对象的 this 指针,这样就可以访问类的成员变量和成员函数。

class MyClass {
public:int value = 42;void printValue() {auto lambda = [this]() {std::cout << "Value: " << value << std::endl;};lambda();}
};int main() {MyClass obj;obj.printValue();  // 输出:Value: 42
}

通过捕获 this 指针,lambda 可以访问和修改对象的成员。

2. 可变的 Lambda 表达式

默认情况下,lambda 是不可修改其捕获的值的。如果需要修改捕获的值,可以在捕获列表后加上 mutable 关键字:

intx = 10;
auto mutableLambda = [x]() mutable {x += 10;std::cout << "Inside lambda: " << x << std::endl;
};
mutableLambda();
std::cout << "Outside lambda: " << x << std::endl;  // 输出:Outside lambda: 10

mutable 使得 x 可以在 lambda 内部被修改,但不会影响外部的 x


六、结语

C++11 的 Lambda 表达式为开发者提供了一种高效、简洁的方式来定义匿名函数,极大地增强了 C++ 的表达能力。理解并掌握 lambda 的使用方式,将有助于写出更现代、更易读的 C++ 代码 参考。

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

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

相关文章

并发——笔试面试总结

1. 进程之间通信的途径有哪些&#xff1f;并说一下他们的通信机制原理 进程间通信的途径包括管道、消息队列、共享内存、信号量、套接字等&#xff0c;以下是几种常见的进程间通信方式及原理&#xff1a; (1) 管道(Pipe) 通信机制原理&#xff1a;管道是一种半双工的通信方式&a…

A0001.主机访问虚拟机中的共享文件完事教程

1. 先在虚拟机中创建一个共享文件夹 2. 在虚拟机的windows系统中查看ip地址 3. 检查网络是否连通 4. 访问虚拟机 5. 登录帐号密码

【JavaEE】——Udp翻译器的实现(回显服务器)

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;引入 1&#xff1a;基本概念 二&#xff1a;UDP socket API使用 1&#xff1a;socke…

正点原子讲解SPI学习,驱动编程NOR FLASH实战

配置SPI传输速度时&#xff0c;需要先失能SPI,__HAL_SPI_DISABLE,然后操作SPI_CR1中的波特率设置位&#xff0c;再使能SPI, NM25Q128驱动步骤 myspi.c #include "./BSP/MYSPI/myspi.h"SPI_HandleTypeDef g_spi1_handler; /* SPI句柄 */void spi1_init(void) {g_spi…

使用Hugging Face中的BERT进行标题分类

使用Hugging Face中的BERT进行标题分类 前言相关介绍出处基本原理优点缺点 前提条件实验环境BERT进行标题分类准备数据集读取数据集划分数据集设置相关参数创建自己DataSet对象计算准确率定义预训练模型定义优化器训练模型保存模型测试模型 参考文献 前言 由于本人水平有限&…

动态规划-简单多状态dp问题——面试题17.16.按摩师

多状态问题的核心就是每个位置不止有一个状态&#xff0c;因此需要多个dp表表示不同状态对应位置的值&#xff0c;然后根据题目考虑特定情况写出状态转移方程即可 1.题目解析 题目来源&#xff1a;面试题17.16.按摩师——力扣 测试用例 2.算法原理 1.状态表示 这里与路径问…

YOLOv11进行图像与视频的目标检测

一、AI应用系统实战项目 项目名称项目名称1.人脸识别与管理系统2.车牌识别与管理系统

【CSS in Depth 2 精译_047】7.2 CSS 响应式设计中的媒体查询原则(上):深入理解媒体查询的类型

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 【第七章 响应式设计】&#xff08;概述&#xff09; 7.1 移动端优先设计原则&#xff08;上篇&#xff09; 7.1.1 创建移动端菜单&#xff08;下篇&#xff09;7.1.2 给视口添加 meta 标签&#xf…

MATLAB - 机器人机械臂设计轨迹规划器

系列文章目录 前言 本示例介绍了一种设计抓取和轨迹规划器的方法,该规划器可用于垃圾箱拣选系统。 在机器人技术中,垃圾箱拣选包括使用机械手从垃圾箱中取出物品。智能垃圾箱拣选是这一过程的高级版本,具有更强的自主性。使用摄像系统感知部件,规划器生成与场景相适应的无碰…

NASA:ARCTAS 区域的二级 FIRSTLOOK 气溶胶产品子集。 它包含气溶胶光学深度和粒子类型,以及相关的大气数据

目录 简介 信息 代码 引用 网址推荐 知识星球 机器学习 MISR L2 FIRSTLOOK Aerosol Product subset for the ARCTAS region V001 简介 这是 ARCTAS 区域的二级 FIRSTLOOK 气溶胶产品子集。 它包含气溶胶光学深度和粒子类型&#xff0c;以及相关的大气数据&#xff0c;…

线程 vs 虚拟线程:深入理解及区别

Java 提供了两种线程机制&#xff1a;普通线程&#xff08;平台线程&#xff09;和 虚拟线程。普通线程是 Java 中经典的并发处理方式&#xff0c;而虚拟线程是随着 Java 21 引入的新特性&#xff0c;旨在提升并发性能和开发体验。本文将详细探讨它们的区别&#xff0c;并帮助你…

关于摩托车一键启动无钥匙进入、智能科技创新

摩托车一键启动无钥匙进入功能 一、工作原理 摩托车的一键启动无钥匙进入功能采用了世界最先进的RFID无线射频技术和最先进的车辆身份编码识别系统&#xff0c;率先应用小型化、小功率射频天线的开发方案&#xff0c;并成功融合了遥控系统和无钥匙系统&#xff0c;沿用了传统…

在 MTT GPU 上使用 llama.cpp 推理

大语言模型因其出色的自然语言理解和生成能力而迅速被广泛使用&#xff0c;llama.cpp 大幅降低了进行大语言模型推理的门槛&#xff0c;MTT GPU 同样也是 llama.cpp 支持的运行平台&#xff0c;能够充分利用硬件的性能来助力用户的大语言模型应用。 本文主要介绍了如何在摩尔线…

【CTF】敏感信息泄露 GIT SVN VIM

在CTF&#xff08;Capture The Flag&#xff09;比赛中&#xff0c;信息泄露是常见的考察方向之一。这类题目通过模拟开发人员的疏忽或系统配置的失误&#xff0c;导致敏感文件或数据被泄露。信息泄露题目通常相对简单&#xff0c;但能帮助参赛者掌握如何从公开的信息中获取潜在…

出处不详 取数游戏

目录 取数游戏题目描述背景输入输出数据范围 题解解法优化 打赏 取数游戏 题目描述 背景 两人将 n n n个正整数围成一个圆环&#xff0c;规则如下&#xff1a; 第一名玩家随意选取数字&#xff1b;第二名玩家从与第一名玩家相邻的两个数字中选择一个&#xff1b;而后依次在…

用Arduino单片机制作一个简单的音乐播放器

Arduino单片机上有多个数字IO针脚&#xff0c;可以输出数字信号&#xff0c;用于驱动发声器件&#xff0c;从而让它发出想要的声音。蜂鸣器是一种常见的发声器件&#xff0c;通电后可以发出声音。因此&#xff0c;单片机可以通过数字输出控制蜂鸣器发出指定的声音。另外&#x…

【尚硅谷】FreeRTOS学笔记(更新中更新时间2024.10.12)

在网上看到的一段很形象的描述&#xff0c;放在这里给大家娱乐一下。 裸机开发&#xff1a;n个人拉屎&#xff0c;先进去一个拉完&#xff0c;下一个再来。看门狗&#xff1a;如果有人拉完屎还占着&#xff0c;茅坑刷视频&#xff0c;把他拖出去中断系统&#xff1a;n个人拉屎&…

Python | Leetcode Python题解之第477题汉明距离总和

题目&#xff1a; 题解&#xff1a; class Solution:def totalHammingDistance(self, nums: List[int]) -> int:n len(nums)ans 0for i in range(30):c sum(((val >> i) & 1) for val in nums)ans c * (n - c)return ans

数通--3

一、动态路由 内部 路由器之间要互联互通&#xff0c;必须遵循相同的协议 企业内部用 IGP&#xff0c;企业之间用BGP RIP&#xff08;已淘汰&#xff0c;不考&#xff09; 距离就是长短&#xff0c;矢量就是方向&#xff0c;即路由的出接口 一台路由器 A 配好RIP&#xff0c;…

SQL INNER JOIN:深入解析与实际应用

SQL INNER JOIN:深入解析与实际应用 引言 在关系型数据库管理系统中,SQL(Structured Query Language)是一种用于管理和操作数据库的标准编程语言。SQL INNER JOIN 是一种常用的查询技术,用于结合两个或多个数据库表中的相关行。本文将深入探讨 SQL INNER JOIN 的概念、语…