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

匿名函数作递归函数引用

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

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

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.lryc.cn/news/585740.html

相关文章:

  • 声明式 vs 编程式:Spring事务管理全对比
  • Prometheus+Grafana部署及企业微信邮件/群消息告警通知配置
  • linux系统-----Redis数据库基础
  • 迭代器(c++)、智能指针
  • LDO选型
  • Rust基础-part2-变量和可变类型
  • LVS-NAT模式配置
  • 期望和方差的计算
  • 深度学习×第8卷:优化器与训练流程进阶——她开始跑起来,学着一次次修正自己
  • 深度体验飞算JavaAI:一场Java开发效率的革命
  • 百度2026届校招开启,大规模发力AI的百度未来何在?
  • Telnet远程连接实验(Cisco)
  • Redis事务失败的处理机制与处理方案
  • 日历插件-FullCalendar的详细使用
  • C++:非类型模板参数,模板特化以及模板的分离编译
  • 【整数大求余草稿】2022-3-7
  • 进制转换原理与实现详解
  • Qt中QGraphicsView类应用解析:构建高效2D图形界面的核心技术
  • vue table 自定义处理 key 作为 表头
  • AUTOSAR进阶图解==>AUTOSAR_SWS_IOHardwareAbstraction
  • [精选]如何解决pip安装报错ModuleNotFoundError: No module named ‘subprocess’问题
  • Matlab裁剪降水数据:1km掩膜制作实战
  • C++STL-list
  • 这个方法的目的是检查一个给定的项目ID(projectId)是否在当前数据库中被使用(搜索全库)
  • 四、神经网络——正则化方法
  • VLM-R1 + GRPO 算法完整复现全过程日志
  • Linux修炼:权限
  • SpringCloud【OpenFeign】
  • 学习日记-spring-day46-7.11
  • 伺服驱动控制CANopen协议