Lambda 表达式是 C++11 引入的重要特性,它允许我们在代码中定义匿名函数,极大地简化了代码编写,尤其是在使用 STL 算法和多线程编程时。本文将详细介绍 Lambda 表达式的语法、特性及实际应用场景。

什么是 Lambda 表达式?

       Lambda 表达式(也称为 lambda 函数)是一种匿名函数,即没有函数名的函数。它可以捕获周围作用域中的变量,并且可以像对象一样被传递和使用。Lambda 表达式的引入主要是为了简化函数对象的使用,特别是在需要短小函数作为参数的场景。

Lambda 表达式的基本结构如下:

[capture-list](parameter-list) mutable(optional) exception-specification(optional) -> return-type(optional) {// 函数体
}

各部分含义:

  • capture-list:捕获列表,指定从周围作用域捕获哪些变量及其捕获方式
  • parameter-list:参数列表,与普通函数的参数列表类似
  • mutable:可选关键字,允许在 lambda 体内修改按值捕获的变量
  • exception-specification:可选,指定 lambda 可能抛出的异常
  • return-type:可选,指定返回类型,若省略,编译器会自动推导
  • 函数体:lambda 的执行代码

捕获列表详解

捕获列表决定了 lambda 表达式可以访问外部作用域中的哪些变量,以及如何访问(按值或按引用)。

捕获方式

  1. 按值捕获[var] 或 [=]

    • [var]:仅按值捕获变量 var
    • [=]:按值捕获所有使用到的外部变量
  2. 按引用捕获[&var] 或 [&]

    • [&var]:仅按引用捕获变量 var
    • [&]:按引用捕获所有使用到的外部变量
  3. 混合捕获

    • [=, &var]:除 var 按引用捕获外,其余按值捕获
    • [&, var]:除 var 按值捕获外,其余按引用捕获
  4. 空捕获列表[]

    • 不捕获任何外部变量

捕获示例:

#include <iostream>int main() {int a = 10, b = 20;// 空捕获列表,不能访问a和bauto lambda1 = []() {std::cout << "Lambda1: 不捕获任何变量" << std::endl;};// 按值捕获a,按引用捕获bauto lambda2 = [a, &b]() {// a = 100; 错误:按值捕获的变量默认是const,不能修改b = 200;   // 正确:可以修改按引用捕获的变量std::cout << "Lambda2: a=" << a << ", b=" << b << std::endl;};// 按值捕获所有外部变量auto lambda3 = [=]() {std::cout << "Lambda3: a=" << a << ", b=" << b << std::endl;};// 按引用捕获所有外部变量auto lambda4 = [&]() {a = 100;b = 300;std::cout << "Lambda4: a=" << a << ", b=" << b << std::endl;};// 混合捕获:除b按值外,其余按引用auto lambda5 = [&, b]() {a = 200;// b = 400; 错误:b是按值捕获的std::cout << "Lambda5: a=" << a << ", b=" << b << std::endl;};lambda1();lambda2();lambda3();lambda4();lambda5();return 0;
}

mutable 关键字

        默认情况下,按值捕获的变量在 lambda 体内是只读的。使用mutable关键字可以允许修改按值捕获的变量:

#include <iostream>int main() {int x = 10;// 不使用mutable,不能修改按值捕获的xauto lambda1 = [x]() {// x = 20; 错误std::cout << "lambda1: x=" << x << std::endl;};// 使用mutable,可以修改按值捕获的x(但不会影响外部的x)auto lambda2 = [x]() mutable {x = 20;std::cout << "lambda2内部: x=" << x << std::endl;};lambda1();lambda2();std::cout << "外部: x=" << x << std::endl;  // 仍然是10return 0;
}

返回类型

       Lambda 表达式的返回类型通常可以由编译器自动推导,但在某些情况下(如有多条 return 语句且类型可能不同),需要显式指定返回类型: 

#include <iostream>int main() {// 自动推导返回类型为intauto add = [](int a, int b) {return a + b;};// 显式指定返回类型auto divide = [](double a, double b) -> double {if (b == 0) return 0;return a / b;};std::cout << "3 + 5 = " << add(3, 5) << std::endl;std::cout << "10 / 3 = " << divide(10, 3) << std::endl;return 0;
}

Lambda 表达式的实际应用场景:

1. 作为 STL 算法的参数

这是 Lambda 表达式最常见的用途,尤其适合简短的谓词函数:

#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6};// 使用lambda排序(降序)std::sort(numbers.begin(), numbers.end(), [](int a, int b) {return a > b;});// 使用lambda过滤并打印std::cout << "大于3的元素: ";std::for_each(numbers.begin(), numbers.end(), [](int n) {if (n > 3) {std::cout << n << " ";}});std::cout << std::endl;return 0;
}

2. 在多线程中使用

Lambda 表达式非常适合作为线程函数:

#include <iostream>
#include <thread>
#include <vector>int main() {std::vector<std::thread> threads;int shared_data = 0;// 创建5个线程for (int i = 0; i < 5; ++i) {// 捕获i(按值)和shared_data(按引用)threads.emplace_back([i, &shared_data]() {std::cout << "线程 " << i << " 启动" << std::endl;// 模拟工作for (int j = 0; j < 1000000; ++j) {shared_data++;}std::cout << "线程 " << i << " 结束" << std::endl;});}// 等待所有线程完成for (auto& t : threads) {t.join();}std::cout << "最终shared_data值: " << shared_data << std::endl;return 0;
}

3. 作为函数返回值

Lambda 表达式可以被包装在std::function中作为函数返回值:

#include <iostream>
#include <functional>// 返回一个lambda表达式
std::function<int(int)> make_multiplier(int factor) {return [factor](int x) {return x * factor;};
}int main() {auto double_it = make_multiplier(2);auto triple_it = make_multiplier(3);std::cout << "5的两倍: " << double_it(5) << std::endl;   // 10std::cout << "5的三倍: " << triple_it(5) << std::endl;   // 15return 0;
}

4. 在条件判断中使用

可以在需要临时函数的地方直接定义 lambda:

#include <iostream>
#include <vector>
#include <string>int main() {std::vector<std::string> words = {"apple", "banana", "cherry", "date", "elderberry"};int min_length = 5;// 查找第一个长度小于min_length的单词auto it = std::find_if(words.begin(), words.end(), [min_length](const std::string& word) {return word.length() < min_length;});if (it != words.end()) {std::cout << "第一个短单词: " << *it << std::endl;  // 输出 "date"}return 0;
}

C++14 及以后版本的增强

C++14 对 lambda 表达式进行了一些有用的增强:

  1. 泛型 lambda:允许使用auto作为参数类型,使 lambda 更加灵活
// 泛型lambda,可以接受任何类型的参数
auto sum = [](auto a, auto b) {return a + b;
};int main() {std::cout << sum(3, 5) << std::endl;       // 8std::cout << sum(3.5, 2.5) << std::endl;   // 6.0std::cout << sum(std::string("Hello "), std::string("World")) << std::endl;  // Hello Worldreturn 0;
}
  1. 初始化捕获:可以在捕获列表中创建和初始化新变量

#include <iostream>
#include <memory>int main() {// 初始化捕获:创建一个unique_ptr并捕获它auto lambda = [ptr = std::make_unique<int>(42)]() {std::cout << "值: " << *ptr << std::endl;};lambda();  // 输出:值: 42return 0;
}

注意事项

  1. 生命周期管理:按引用捕获局部变量时要特别小心,确保 lambda 在变量销毁后不再被调用

  2. 性能考量:lambda 表达式通常会被编译器优化,性能与普通函数相当,但过度使用复杂的 lambda 可能影响可读性

  3. 调试困难:匿名特性使得调试时可能难以识别特定的 lambda 函数

  4. 捕获列表的复杂性:过度复杂的捕获列表可能导致代码难以理解和维护

总结

        Lambda 表达式是 C++ 中一个非常强大的特性,它提供了一种简洁、灵活的方式来定义匿名函数。通过合理使用 lambda 表达式,我们可以编写更简洁、更易读的代码,特别是在使用 STL 算法和多线程编程时。

       掌握 lambda 表达式的关键在于理解捕获列表的工作方式,以及如何在不同场景下选择合适的捕获方式。随着 C++ 标准的不断发展,lambda 表达式的功能也在不断增强,成为现代 C++ 编程中不可或缺的工具。

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

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

相关文章

Spring Boot注解详解

文章目录前言1. 核心启动注解SpringBootApplicationEnableAutoConfigurationSpringBootConfiguration2. 组件注解Component及其衍生注解ComponentServiceRepositoryControllerRestController3. 依赖注入注解AutowiredQualifierPrimary4. Web相关注解请求映射注解RequestMapping…

Web开发:ABP框架12——中间件Middleware的创建和使用

一、简介中间件可以用于鉴权、日志&#xff0c;拦截器可以用于指定方法或url的业务逻辑处理&#xff0c;两者分工不同&#xff0c;实现效果相似&#xff0c;先执行中间件&#xff0c;后执行拦截器&#xff0c;再到WebAPI接口。二、示例一个Token验证中间件三、代码1.Startup.cs…

京东商品评论如何获取?API接口实战指南

一、API接入准备1. 注册开发者账号访问京东开放平台&#xff1a;前往京东开放平台注册账号&#xff0c;完成企业或个人实名认证。创建应用&#xff1a;在控制台创建应用&#xff0c;获取App Key和App Secret&#xff08;用于签名认证&#xff09;。2. 申请API权限搜索接口&…

leetcode-sql-627变更性别

题目&#xff1a; Salary 表&#xff1a; --------------------- | Column Name | Type | --------------------- | id | int | | name | varchar | | sex | ENUM | | salary | int | --------------------- id 是这个表的主键…

【学习路线】C#企业级开发之路:从基础语法到云原生应用

一、C#基础入门&#xff08;1-2个月&#xff09; &#xff08;一&#xff09;开发环境搭建Visual Studio安装配置 Visual Studio Community&#xff1a;免费版本&#xff0c;功能完整Visual Studio Code&#xff1a;轻量级&#xff0c;跨平台支持JetBrains Rider&#xff1a;专…

Planning Agent:基于大模型的动态规划与ReAct机制,实现复杂问题自适应执行求解

引言 在当今数据驱动的商业环境中&#xff0c;企业面临着日益复杂的决策问题。传统的数据分析工具往往难以应对多步骤、多依赖的复杂问题求解。例如&#xff0c;当企业需要分析"北美市场 Q1-Q2 主要产品的销售增长趋势并识别关键驱动因素"时&#xff0c;传统工具可能…

人该怎样活着呢?55

人该怎样活着呢&#xff1f; A思考现实问题并记录自己的灵感 。【生活的指南针】 &#xff08;20250212&#xff09; a1如何思考&#xff1f; 当有人问他用什么方法得到那么多发现时&#xff0c;牛顿说&#xff1a;“我只不过对于一件事情&#xff0c;总是花很长时间很热…

rtthread - V5.1.0版本 HOOK 钩子函数总结

rtthread - V5.1.0版本 钩子函数 相对于V4.0.3版本做了很大的修改和优化&#xff1a;旧版本 V4.0.3&#xff1a;rt_thread_inited_sethook(thread_inited_hook);rt_thread_deleted_sethook(thread_deleted_hook);rt_scheduler_sethook(scheduler_hook);新版本 V5.1.0&#xff1…

Python特性:装饰器解决数据库长时间断连问题

前言 在基于 Python 的 Web 应用开发里&#xff0c;数据库连接是极为关键的一环。不过&#xff0c;像网络波动、数据库服务器维护这类因素&#xff0c;都可能造成数据库长时间断连&#xff0c;进而影响应用的正常运作。本文将详细介绍怎样运用 retry_on_failure 装饰器来解决数…

疗愈之手的智慧觉醒:Deepoc具身智能如何重塑按摩机器人的触觉神经

疗愈之手的智慧觉醒&#xff1a;Deepoc具身智能如何重塑按摩机器人的触觉神经康复中心的理疗室内&#xff0c;一位运动员正俯卧在治疗床上。机械臂的硅胶触头沿腰背肌群缓缓移动&#xff0c;当传感器捕捉到竖脊肌的异常僵直时&#xff0c;触头自动切换高频震颤模式&#xff1b;…

webpack将组件vue进行编译混淆,并能正常使用编译之后的文件

介绍: 我们在开发的过程中有很多组件都需要复用,特别是我们耗费了好几天时间写出来的组件,比如自己写的表格组件,流程图组件等。总之都是自己不断测试,不断编写耗费了大把的精力写的。直接用到自己的项目中倒是无所谓,如果是把自己写的组件给别人,这里就涉及到自己的劳动…

onenote千年老bug,字体bug (calibri微软雅黑) 的解决

一、如何改这个bug&#xff08;bug是什么在后文&#xff09;一、注意1、有些onenote可能是版本问题&#xff0c;即使提供了设置默认字体的选项&#xff0c;但按教程设置后还是不work&#xff0c;建议升级版本2、亲身测过这个方法是可行的&#xff0c;如果不行&#xff0c;考虑下…

麒麟信安参编的三项软件供应链安全团体标准发布

日前&#xff0c;由中国电子商会正式发布了T/CECC 39—2025《信息安全技术 软件供应链管理规范》、T/CECC 40—2025《信息安全技术 软件供应链开源组件检测要求》以及 T/CECC 41—2025《信息安全技术 软件供应链软件产品检测要素和方法》三项重要团体标准。麒麟信安结合自身在软…

Django ORM系统

1. ORM基础概念1.1 什么是ORM&#xff1f;ORM&#xff08;Object Relational Mapping&#xff0c;对象关系映射&#xff09;是一种编程技术&#xff0c;用于在面向对象编程语言中实现不同类型系统的数据转换。在Django中&#xff0c;ORM充当业务逻辑层和数据库层之间的桥梁。核…

Tailwind CSS中设定宽度和高度的方法

在 Tailwind CSS 中&#xff0c;设定元素的宽度&#xff08;width&#xff09;和高度&#xff08;height&#xff09;有多种方式&#xff0c;涵盖固定值、相对值、响应式调整等。以下是完整的方法分类及示例&#xff1a;一、固定宽度 / 高度类以 4px (0.25rem) 为单位递增&…

Java行为型模式---备忘录模式

备忘录模式基础概念备忘录模式&#xff08;Memento Pattern&#xff09;是一种行为型设计模式&#xff0c;其核心思想是在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象之外保存这个状态&#xff0c;以便后续可以将该对象恢复到先前保存的状态…

后端参数校验

前端给后端传输数据&#xff0c;有时候参数需要校验&#xff0c;我们自己写代码会比较麻烦&#xff0c;我们可以使用springboot为我们提供的注解&#xff0c;降低这些没有必要的代码开发。1.引入依赖<dependency><groupId>org.springframework.boot</groupId>…

C++ - 仿 RabbitMQ 实现消息队列--服务端核心模块实现(一)

目录 日志打印工具 实用 Helper 工具 sqlite 基础操作类 字符串操作类 UUID 生成器类 文件基础操作 文件是否存在判断 文件大小获取 读文件 写文件 文件重命名 文件创建/删除 父级目录的获取 目录创建/删除 附录&#xff08;完整代码&#xff09; 日志打印工具 为了便…

C语言:第07天笔记

C语言&#xff1a;第07天笔记 内容提要 循环结构 break与continue 综合案例《猜拳游戏》数组 数组的概念一维数组流程控制 break与continue break 功能&#xff1a; ① 用在switch中&#xff0c;用来跳出switch中的case语句&#xff1b;如果case没有break&#xff0c;可能会产生…

qt 中英文翻译 如何配置和使用

qt 中英文翻译 如何配置和使用 1. 在.pro文件中添加TRANSLATIONS 在你的 .pro 文件&#xff08;比如 HYAC_AAF_HOST.pro&#xff09;中添加&#xff1a; TRANSLATIONS \ zh\_CN.ts en\_US.ts这会告诉Qt项目你要支持中文和英文。 2. 提取可翻译文本&#xff08;生成ts文件&#…