当前位置: 首页 > ds >正文

【读书笔记】《Effective Modern C++》第六章 Lambda Expressions

《Effective Modern C++》第六章 Lambda Expressions

Lambda 表达式是 C++11 引入的一项革命性特性,使得函数对象的定义和使用更加简洁灵活。到了 C++14 和 C++17,Lambda 又逐步具备了更强的表达力(如 init capture、泛型 Lambda、constexpr lambda 等)。


Item 31:避免使用默认捕获模式(Avoid default capture modes)

背景

Lambda 表达式可以通过以下方式捕获外部变量:

  • [] 不捕获任何外部变量;
  • [=] 默认按值捕获所有可见变量;
  • [&] 默认按引用捕获所有可见变量;
  • [x, &y] 显式捕获 x 按值,y 按引用。

问题

默认捕获模式易隐藏错误和副作用。

例:

void example() {int x = 0;auto lambda = [=]() { std::cout << x << std::endl; };x = 42;lambda(); // 输出 0,但你是否意识到 x 是按值复制?
}

或:

std::vector<std::function<void()>> actions;
for (int i = 0; i < 5; ++i) {actions.push_back([&]() { std::cout << i << std::endl; }); // 所有输出5
}

由于 i 被引用捕获,Lambda 延迟执行时访问的是同一个变量。

建议

始终显式捕获所需变量,不使用 [=][&]

auto lambda = [x]() { std::cout << x << std::endl; };

Item 32:使用 init capture 将对象移动进闭包(Use init capture to move objects into closures)

问题

传统 C++11 Lambda 无法直接将 std::unique_ptr 或其他移动专属类型传入闭包:

std::unique_ptr<int> up(new int(42));
auto lambda = [up]() { ... }; // 错误:复制被禁

C++14 引入 init capture(初始化捕获),可支持移动语义。

解决方式(C++14+)

auto lambda = [up = std::move(up)]() {*up += 1;std::cout << *up << std::endl;
};

这样 up 被移动进闭包,生命周期受控于 Lambda 本身,避免复制和潜在悬空指针。

额外用途

  • 可以用 init capture 构造并传入一个临时对象;
  • 适用于绑定外部资源或限定作用域对象。

Item 33:在 auto&& 参数上使用 decltype + std::forward(Use decltype on auto&& parameters to std::forward them)

背景

C++14 支持泛型 Lambda

auto lambda = [](auto&& param) {use(param);  // 是否转发了值类别?
};

此时 param 是一个 universal reference,但 param 是左值,调用 use() 时会发生不必要的拷贝或失效。

正确方式:配合 decltypestd::forward

auto lambda = [](auto&& param) {use(std::forward<decltype(param)>(param));
};

这样可以保留调用者传入参数的值类别,实现完美转发。

实际场景

  • 泛型工具函数中的转发器;
  • 延迟执行、缓存包装器;
  • 函数适配器。

注意

  • auto&& 本身不能传递值类别信息;
  • decltype(param) 得到的类型才能判断是左值引用还是右值引用;
  • 对 universal reference 参数始终用 std::forward<decltype(param)>

Item 34:优先使用 Lambda 而不是 std::bind(Prefer lambdas to std::bind)

背景

std::bind 用于部分函数绑定,在 C++98 和 C++11 被广泛使用,但语法复杂,类型不透明,可读性差。

例:

auto f = std::bind(&MyClass::method, obj, _1, 42);
  • _1 来自 <functional>,不直观;
  • 产生的类型复杂,难调试;
  • 编译错误难排查。

替代方案:Lambda 表达式

auto f = [obj](int x) {obj.method(x, 42);
};
  • 更易读、更明确;
  • 支持捕获、移动、嵌套等功能;
  • 更易于调试和出错时诊断。

实践建议

始终用 Lambda 替代 std::bind,除非遇到非常规的模板绑定逻辑或特定 legacy 接口。


总结

Lambda 表达式是现代 C++ 最强大也是最常用的语法工具之一。本章提供了如下关键建议:

条目建议
31避免 [=][&],改用显式捕获
32使用 init capture ([x = std::move(x)]) 移动资源进闭包
33对泛型参数使用 decltype + std::forward 保持值类别
34优先使用 Lambda,避免使用 std::bind

通过正确使用 Lambda,你不仅可以简化代码、提升性能,更能避免因隐式捕获、复制、转发失败而带来的微妙错误。这是写出现代、高效、健壮 C++ 的基础能力之一。

http://www.xdnf.cn/news/15489.html

相关文章:

  • Windows 常用命令
  • vue防内存泄漏和性能优化浅解
  • 如何自动化处理TXT日志,提升工作效率新方式
  • RabbitMQ队列的选择
  • 03.Python 字符串中的空白字符处理
  • Springboot实现一个接口加密
  • 华为交换机 undo negotiation auto功能(华为交换机端口接光纤两端起不来)
  • 【Complete Search】-基础完全搜索-Basic Complete Search
  • JAVA 项目工程化实践
  • fatal: active `post-checkout` hook found during `git clone`
  • v-for中key值的作用:为什么我总被要求加这个‘没用的‘属性?
  • 大小为 K 且平均值大于等于阈值的子数组数目
  • “找到一个或多个多重定义的符号“(LNK2005 或 LNK1169)
  • 006_测试评估与安全实践
  • 深入理解 LangChain:AI 应用开发的全新范式
  • 面试150 填充每个节点的下一个右侧节点指针Ⅱ
  • 第一个Flink 程序 WordCount,词频统计(批处理)
  • ReAct论文解读(1)—什么是ReAct?
  • AI大模型计数能力的深度剖析:从理论缺陷到技术改进
  • Java行为型模式---观察者模式
  • macOS - Chrome 关闭自动更新
  • c语言初阶 结构体
  • 基于Flink的实时开发平台-Dinky
  • v-show和v-if的区别
  • 【C++】auto关键字 C++入门(5)
  • 数据结构(8)——二叉树(2)
  • HarmonyOS 获取设备位置信息开发指导
  • 每天一个前端小知识 Day 30 - 前端文件处理与浏览器存储机制实践
  • Rust 模块系统:控制作用域与私有性
  • 《[系统底层攻坚] 张冬〈大话存储终极版〉精读计划启动——存储架构原理深度拆解之旅》-系统性学习笔记(适合小白与IT工作人员)