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

匿名函数作递归函数引用

核心问题:匿名函数没有“名字”

常规的递归函数可以调用自己,是因为它有自己的名字:

int factorial(int n) {if (n <= 1) return 1;return n * factorial(n - 1); // <-- 通过名字 'factorial' 调用自己
}

但 Lambda 表达式是匿名的,在它的函数体内部,它不知道自己的“名字”是什么,因此无法直接引用自己。

auto factorial_lambda = [](int n) {if (n <= 1) return 1;// return n * ???(n - 1); // <-- ??? 这里该写什么?
};

为了解决这个问题,我们需要一种方法让 Lambda 能够“找到”并调用它自身。主要有以下几种方法,你需要根据你的 C++ 标准版本和具体场景来选择。


方法一:使用 std::function (C++11 及以上)

这是最经典、最通用的方法。思路是先创建一个 std::function 对象,然后让 Lambda 按引用捕获这个 std::function 对象本身。

#include <iostream>
#include <functional>int main() {// 1. 声明一个 std::function 对象来包裹 Lambdastd::function<int(int)> factorial;// 2. 定义 Lambda,并【按引用】捕获 factorialfactorial = [&factorial](int n) -> int {if (n <= 1) {return 1;} else {// 3. 通过捕获到的 factorial 对象实现递归调用return n * factorial(n - 1);}};std::cout << factorial(5) << std::endl; // 输出 120return 0;
}
⚠️ 注意事项:

  1. 必须按引用捕获 ([&factorial]):这是此方法最关键的一点!如果你按值捕获 ([=][factorial]),在 Lambda 被创建时,它会试图复制 factorial 对象,但此时 factorial 自身还没有被赋值(它正在被定义的过程中),这会导致未定义行为(通常是程序崩溃)。按引用捕获则没有这个问题,因为它捕获的是 factorial 这个“容器”的引用,而不是它当时的内容。

  2. 性能开销std::function 为了通用性,使用了类型擦除 (Type Erasure) 技术,可能会有微小的性能开销(例如虚函数调用或堆分配)。对于性能极其敏感的场景,这可能不是最佳选择。


方法二:使用泛型 Lambda (C++14 及以上)

这是一种更现代、更高效的方法,它巧妙地利用了泛型 Lambda 的特性,将 Lambda 自身作为参数传递给自己。

#include <iostream>int main() {// 1. 定义一个泛型 Lambda,第一个参数用于接收“自己”auto factorial = [](auto&& self, int n) -> int { // 这里使用的是转发引用,根据传入的函数自动推导。if (n <= 1) {return 1;} else {// 2. 通过传入的 self 参数实现递归return n * self(self, n - 1);}};// 3. 首次调用时,需要将 Lambda 对象自身作为第一个参数传入std::cout << factorial(factorial, 5) << std::endl; // 输出 120return 0;
}

这里的 auto&& self 是一个通用引用(forwarding reference),可以接受任何类型的 self 参数,非常灵活。

⚠️ 注意事项:

  1. 调用形式略显奇怪:第一次调用 factorial(factorial, 5) 和递归调用 self(self, n-1) 的形式需要习惯一下。

  2. 高性能:这种方法避免了 std::function 的开销,编译器可以更好地进行内联和优化,性能几乎等同于普通的递归函数。


方法三:使用 deducing this (C++23 及以上)

C++23 带来了一个终极解决方案:deducing this。它允许 Lambda 显式地将自己的闭包对象(closure object)作为参数,从而可以直接引用自己,语法非常优雅。

#include <iostream>int main() {// 1. 使用 `this auto&&` 将 Lambda 自身作为参数auto factorial = [](this auto&& self, int n) -> int { if (n <= 1) {return 1;} else {// 2. 直接调用 self 即可,无需再传递它return n * self(n - 1);}};std::cout << factorial(5) << std::endl; // 输出 120return 0;
}

⚠️ 注意事项:

  1. 需要 C++23 支持:这是最新的标准,你需要一个支持 C++23 的编译器(例如 GCC 12+, Clang 15+)。

  2. 最优雅的方案:它的调用方式 factorial(5)self(n-1) 与普通函数完全一致,是未来编写递归 Lambda 的最佳方式。

总结与建议

方法

核心思想

优点

缺点

适用场景

std::function

按引用捕获 std::function 包装器

✅ 通用,C++11即可用
✅ 调用语法自然 f(5)

⚠️ 必须按引用捕获
❌ 有轻微性能开销

兼容旧标准 (C++11) 或需要将 Lambda 存入 std::function 容器的场合。

泛型 Lambda

将自身作为参数传递

✅ 高性能,无额外开销
✅ C++14即可用

❌ 调用和递归语法稍显繁琐 f(f, 5)

性能敏感,且使用 C++14/17/20 的现代 C++ 项目。(当前主流推荐)

deducing this

语言层面直接支持自引用

✅ 语法最简洁、最优雅
✅ 高性能

❌ 需要 C++23 编译器支持

使用 C++23 及以上标准的项目。(未来最佳实践)

一句话总结:

写递归 Lambda 时,首要记住它不能直接调用自己。然后根据你的 C++ 版本,优先选择 C++23 的 deducing this,其次是 C++14 的泛型 Lambda,最后才是 C++11 的 std::function 方法(并牢记必须按引用捕获)。

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

相关文章:

  • uniapp制作一个视频播放页面
  • C++11中的std::minmax与std::minmax_element:原理解析与实战
  • WIFI协议全解析06:Beacon帧、Probe帧你必须懂,搞WiFi通信绕不开它们
  • 【理念●体系】Windows AI 开发环境搭建实录:六层架构的逐步实现与路径治理指南
  • SEQUENCE在RAC多实例开启CACHE的NEXTVAL数值乱序问题
  • 从代码学习深度强化学习 - PPO PyTorch版
  • Go语言WebSocket编程:从零打造实时通信利器
  • Linux操作系统从入门到实战:怎么查看,删除,更新本地的软件镜像源
  • 蔚来测开一面:HashMap从1.7开始到1.8的过程,既然都解决不了并发安全问题,为什么还要进一步解决环形链表的问题?
  • Spring的事务控制——学习历程
  • HarmonyOS NEXT端云一体化开发初体验
  • [Dify] -基础入门4-快速创建你的第一个 Chat 应用
  • 牛客:HJ17 坐标移动[华为机考][字符串]
  • Leaflet面试题及答案(1-20)
  • [实战]调频三角波和锯齿波信号生成(完整C代码)
  • 深入浅出:什么是MCP(模型上下文协议)?
  • 力扣网编程134题:加油站(双指针)
  • C++中柔性数组的现代化替代方案:从内存布局优化到标准演进
  • Debian:从GNOME切换到Xfce
  • 扫描文件 PDF / 图片 纠斜 | 图片去黑边 / 裁剪 / 压缩
  • I2C集成电路总线
  • Semi-Supervised Single-View 3D Reconstruction via Prototype Shape Priors
  • 基于Java Spring Boot开发的旅游景区智能管理系统 计算机毕业设计源码32487
  • linux网络编程之单reactor模型(一)
  • Python 数据建模与分析项目实战预备 Day 2 - 数据构建与字段解析(模拟简历结构化数据)
  • 【前端】【组件库开发】【原理】【无框架开发】现代网页弹窗开发指南:从基础到优化
  • GNhao,获取跨境手机SIM卡跨境通信新选择!
  • 手机恢复出厂设置怎么找回数据?Aiseesoft FoneLab for Android数据恢复工具分享
  • Java中的泛型继承
  • 深度学习篇---昇腾NPUCANN 工具包