C++14 中的递归 lambda 函数 [重复]

2024-01-22

在 C++11 中编写递归 lambda 函数有一个经常重复的“技巧”,如下所示:

std::function<int(int)> factorial;
factorial = [&factorial](int n)
{ return n < 2 ? 1 : n * factorial(n - 1); };

assert( factorial(5) == 120 );

(e.g. C++0x 中的递归 lambda 函数 https://stackoverflow.com/q/2067988/726300.)

但这种技术有两个直接的缺点:std::function<Sig>对象被绑定(通过引用捕获)到一个非常特定的std::function<Sig>对象(这里,factorial)。这意味着生成的函子通常不能从函数返回,否则引用将悬空。

另一个(尽管不太直接)问题是使用std::function通常会阻止编译器优化,这是其实现中需要类型擦除的副作用。这不是假设,可以很容易地进行测试。

在递归 lambda 表达式确实很方便的假设情况下,有没有办法解决这些问题?


问题的关键是在 C++ lambda 表达式中implicit this参数将始终引用表达式的封闭上下文的对象(如果存在),而不是由 lambda 表达式生成的函子对象。

借一片叶子匿名递归 http://en.wikipedia.org/wiki/Anonymous_recursion(有时也称为“开放递归”),我们可以使用 C++14 的通用 lambda 表达式重新引入explicit参数来引用我们的递归函子:

auto f = [](auto&& self, int n) -> int
{ return n < 2 ? 1 : n * self(/* hold on */); };

呼叫者现在有一个新的负担,即拨打以下形式的电话:f(f, 5)。由于我们的 lambda 表达式是自引用的,它实际上是自身的调用者,因此我们应该有return n < 2 ? 1 : n * self(self, n - 1);.

由于在第一个位置显式传递函子对象本身的模式是可以预测的,因此我们可以重构这个丑陋的疣:

template<typename Functor>
struct fix_type {
    Functor functor;

    template<typename... Args>
    decltype(auto) operator()(Args&&... args) const&
    { return functor(functor, std::forward<Args>(args)...); }

    /* other cv- and ref-qualified overloads of operator() omitted for brevity */
};

template<typename Functor>
fix_type<typename std::decay<Functor>::type> fix(Functor&& functor)
{ return { std::forward<Functor>(functor) }; }

这允许人们写:

auto factorial = fix([](auto&& self, int n) -> int
{ return n < 2 ? 1 : n * self(self, n - 1); });

assert( factorial(5) == 120 );

我们成功了吗?自从fix_type<F>对象包含自己的函子,每次调用时都会将其传递给它,因此永远不存在悬空引用的风险。所以我们的factorial对象确实可以毫无麻烦地无限制地从函数中复制、移入和移出。

除了...虽然“外部”呼叫者可以轻松拨打以下形式的电话factorial(5),事实证明,在我们的 lambda 表达式中,递归调用仍然是这样的self(self, /* actual interesting args */)。我们可以通过改变来改进这一点fix_type不通过functor到它自己,但是通过传递*this反而。也就是说,我们传入fix_type负责在第一个位置传递正确的“隐式作为显式”参数的对象:return functor(*this, std::forward<Args>(args)...);。那么递归就变成了n * self(n - 1),应该如此。

最后,这是生成的代码main使用return factorial(5);而不是断言(对于任何一种风格fix_type):

00000000004005e0 <main>:
  4005e0:       b8 78 00 00 00          mov    eax,0x78
  4005e5:       c3                      ret    
  4005e6:       66 90                   xchg   ax,ax

编译器能够优化所有内容,就像使用普通的递归函数一样。


费用是多少?

精明的读者可能已经注意到一个奇怪的细节。在从非泛型 lambda 到泛型 lambda 的过程中,我添加了显式返回类型(即-> int)。怎么会?

这与要推导的返回类型是条件表达式的类型有关,具体类型取决于对self,正在推导哪种类型。快速阅读普通函数的返回类型推导 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3638.html建议按如下方式重写 lambda 表达式应该可行:

[](auto&& self, int n)
{
    if(n < 2) return 1;               // return type is deduced here
    else return n * self(/* args */); // this has no impact
}

GCC 实际上会接受第一种形式的代码fix_type仅(通过的那个functor)。我无法确定抱怨其他表格是否正确(其中*this已通过)。我让读者选择要进行什么权衡:更少的类型推导,或者更少丑陋的递归调用(当然也完全有可能访问任何一种风格)。


GCC 4.9 示例

  • 完整代码,初尝味道 http://coliru.stacked-crooked.com/a/845e539a805f2658
  • 完整代码,第二种味道 http://coliru.stacked-crooked.com/a/596268717ab16c94
  • 完整代码,第一风格,C++11 http://coliru.stacked-crooked.com/a/87812c680208564b
  • 可变参数的示例fix对于一组相互递归的 lambda 表达式 http://coliru.stacked-crooked.com/a/dff32e33a322427f
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++14 中的递归 lambda 函数 [重复] 的相关文章

  • 具有子列表属性映射问题的自动映射器

    我有以下型号 Models public class Dish Required public Int64 ID get set Required public string Name get set Required public str
  • 查找哪些页面不再与写入时复制共享

    假设我在 Linux 中有一个进程 我从中fork 另一个相同的过程 后forking 因为原始进程将开始写入内存 Linux写时复制机制将为进程提供与分叉进程使用的不同的唯一物理内存页 在执行的某个时刻 我如何知道原始进程的哪些页面已被写
  • 进程何时获得 SIGABRT(信号 6)?

    C 中进程获得 SIGABRT 的场景有哪些 该信号是否始终来自进程内部 或者该信号可以从一个进程发送到另一个进程吗 有没有办法识别哪个进程正在发送该信号 abort 向调用进程发送SIGABRT信号 就是这样abort 基本上有效 abo
  • 首先对列表中最长的项目进行排序

    我正在使用 lambda 来修改排序的行为 sorted list key lambda item item lower len item 对包含元素的列表进行排序A1 A2 A3 A B1 B2 B3 B 结果是A A1 A2 A3 B
  • C++:重写已弃用的虚拟方法时出现弃用警告

    我有一个纯虚拟类 它有一个纯虚拟方法 应该是const 但不幸的是不是 该接口位于库中 并且该类由单独项目中的其他几个类继承 我正在尝试使用这个方法const不会破坏兼容性 至少在一段时间内 但我找不到在非常量方法重载时产生警告的方法 以下
  • 显示异常时的自定义错误消息:从客户端检测到潜在危险的 Request.Form 值

    我在我的 Web 应用程序中使用 ASP NET 的登录控件 当发生此异常时 我想在标签上显示一种有趣的错误类型System Web HttpRequestValidationException A potentially dangerou
  • 如何使用recv()检测客户端是否仍然连接(并且没有挂起)?

    我写了一个多客户端服务器程序C on SuSE Linux 企业服务器 12 3 x86 64 我为每个客户端使用一个线程来接收数据 我的问题是 我使用一个终端来运行服务器 并使用其他几个终端来运行服务器telnet到我的服务器 作为客户端
  • 检查算术运算中的溢出情况[重复]

    这个问题在这里已经有答案了 可能的重复 检测 C C 中整数溢出的最佳方法 https stackoverflow com questions 199333 best way to detect integer overflow in c
  • 如何识别 WPF 文本框中的 ValidationError 工具提示位置

    我添加了一个箭头来指示工具提示中的文本框 当文本框远离屏幕边缘时 这非常有效 但是当它靠近屏幕边缘时 工具提示位置发生变化 箭头显示在左侧 Here is the Image Correct as expected since TextBo
  • 基于xsd模式生成xml(使用.NET)

    我想根据我的 xsd 架构 cap xsd 生成 xml 文件 我找到了这篇文章并按照说明进行操作 使用 XSD 文件生成 XML 文件 https stackoverflow com questions 6530424 generatin
  • 如何重置捕获像素的值

    我正在尝试创建一个 C 函数 该函数返回屏幕截图位图中每四个像素的 R G 和 B 值 这是我的代码的一部分 for int ix 4 ix lt 1366 ix ix 4 x x 4 for int iy 3 iy lt 768 iy i
  • 将构建日期放入“关于”框中

    我有一个带有 关于 框的 C WinForms 应用程序 我使用以下方法将版本号放入 关于 框中 FileVersionInfo GetVersionInfo Assembly GetExecutingAssembly Location F
  • 如何一步步遍历目录树?

    我发现了很多关于遍历目录树的示例 但我需要一些不同的东西 我需要一个带有某种方法的类 每次调用都会从目录返回一个文件 并逐渐遍历目录树 请问我该怎么做 我正在使用函数 FindFirstFile FindNextFile 和 FindClo
  • System.Runtime.InteropServices.COMException(0x80040154):[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我在 C 项目中遇到异常 System Runtime InteropServices COMException 0x80040154 检
  • 在类的所有方法之前运行一个方法

    在 C 3 或 4 中可以做到这一点吗 也许有一些反思 class Magic RunBeforeAll public void BaseMethod runs BaseMethod before being executed public
  • 剪贴板在 .NET 3.5 和 4 中的行为有所不同,但为什么呢?

    我们最近将一个非常大的项目从 NET Framework 3 5 升级到 4 最初一切似乎都工作正常 但现在复制粘贴操作开始出现错误 我已经成功制作了一个小型的可复制应用程序 它显示了 NET 3 5 和 4 中的不同行为 我还找到了一种解
  • WinRT 定时注销

    我正在开发一个 WinRT 应用程序 要求之一是应用程序应具有 定时注销 功能 这意味着在任何屏幕上 如果应用程序空闲了 10 分钟 应用程序应该注销并导航回主屏幕 显然 执行此操作的强力方法是在每个页面的每个网格上连接指针按下事件 并在触
  • 用于 C# XNA 的 Javascript(或类似)游戏脚本

    最近我准备用 XNA C 开发另一个游戏 上次我在 XNA C 中开发游戏时 遇到了必须向游戏中添加地图和可自定义数据的问题 每次我想添加新内容或更改游戏角色的某些值或其他内容时 我都必须重建整个游戏或其他内容 这可能需要相当长的时间 有没
  • 匿名结构体作为返回类型

    下面的代码编译得很好VC 19 00 23506 http rextester com GMUP11493 标志 Wall WX Za 与VC 19 10 25109 0 标志 Wall WX Za permissive 这可以在以下位置检
  • 错误:无效使用不完整类型“类 Move”/未定义对 Move::NONE 的引用

    拜托 我不知道为什么这个简单的代码被拒绝 它给了我 2 个编译错误 请帮帮我 I use 代码 块 20 03 我的编译器是GNU GCC 移动 hpp class Move public Move Move int int public

随机推荐

  • 将具有重复键的键值数组转换为具有唯一键和值数组属性的对象数组

    我有一个键 值对数组 键有时会重复 并且每个键的值始终是唯一的 我想将每个唯一的键压缩为一个对象 这样我就有一个键和一个关联值的数组作为属性 有没有方便的 JavaScript 函数可以做到这一点 This pairArray key a
  • 使用 FFmpeg 从大电影创建缩略图需要太长时间

    我正在使用这个 shell 命令从 123 秒的 VIDEO FILE 中制作缩略图并将其保存到 THUMBNAIL FILE 中 ffmpeg i VIDEO FILE r 1 ss 123 f image2 THUMBNAIL FILE
  • 我可以在 GridView 中合并页脚吗?

    我有一个与数据集绑定的 GridView 我有我的页脚 它由列线分隔 我想合并 2 列 我怎么做
  • 将预编译模板与 Handlebars.js 结合使用(jQuery Mobile 环境)

    我在 Handlebars 中的模板预编译方面遇到了一些困难 我的 jQuery Mobile 项目在模板方面变得相当大 我希望预编译我使用的模板 然而 我似乎找不到关于如何使用车把执行此操作的良好解释 例如分步教程 我仍然使用脚本标签将模
  • 如何让只有授权用户才能访问存储在 Amazon S3 中的内容?

    一旦您将内容存储在 S3 中并将其公开 那么每个人都可以访问它 有没有办法让只有授权用户才能访问S3中存储的内容 例如 我有一个允许人们存储文档的网站 服务器将这些文档存储在 S3 中 我希望只有上传该文档的用户才能访问它 我知道我可以将
  • 无法对 DependencyProperty 进行数据绑定

    我有一个带有 DependencyProperty 的 UserControl 我使用数据绑定表达式在主机窗口中设置它的值 但是 它并没有按预期工作 用户控件的代码隐藏片段 public class ViewBase UserControl
  • 带有 JSON 数据的 DataTable

    我正在尝试使用 DataTable 创建一个表 但很难让 DataTable 使用 JSON 对象加载 function getData var request new XMLHttpRequest var json link to my
  • MPMoviePlayerController 与启动 UIWebView 来流电影的优缺点

    我有一位客户拥有 Flash 格式的网络视频内容 我的任务是帮助他们在 iPhone 应用程序中展示视频 我意识到第一步是将这些视频转换为适合 iPhone 的 Quicktime 格式 然后我必须帮助客户弄清楚如何或在哪里托管这些文件 如
  • python列表中的索引错误

    B l append l i A B l 这里是一个列表 我试图向其中附加更多值以使其充当数组 但它仍然给我错误 如列表索引超出范围 如何摆脱它 您的代码存在很多问题 1 the append method 不返回任何内容 所以这样写是没有
  • SQL-如何将 YYYYMM 数字转换为日期

    所以我对 SQL 还很陌生 我有一个充满数字的列 以 YYYYMM 格式列出年份和月份 即 2016 年 7 月的 201607 我想知道是否可以在 SQL 中将其转换为正确的日期格式 我已经做了相当多的研究 并且看到了很多关于将 YYYY
  • 如何发送 zip 文件而不在物理位置创建它?

    我想发送带有 zip 文件附件的电子邮件 我可以发送 pdf 文件 而无需使用 ByteArrayOutputStream 将它们保存在物理位置 但是当我尝试压缩这些文件并发送它时它不起作用 它给出了非法附件的异常 下面是我编写的用于创建
  • 从 iOS 中的离屏 OpenGL 像素缓冲区读取像素 (OopenGL-ES)

    我想从屏幕外 不受 CAEAGLLayer 支持 帧缓冲区读取像素 我创建缓冲区的代码如下所示 glGenFramebuffersOES 1 storeFramebuffer glGenRenderbuffersOES 1 storeRen
  • Woocommerce 订单数量的倍数

    我试图将 woocommerce 限制为仅以 5 10 或 15 的固定数量进行销售 下面的代码片段 我在这个论坛上找到的 允许我将最小数量设置为 5 但我想知道是否有人可以建议是否可以将其修改为允许 5 10 或 15 我感谢您提供的任何
  • 如何找到文本光标的全局位置?

    我想执行一个QMenu http qt nokia com doc 4 0 qmenu html文本光标位置处的对象QPlainTextEdit http doc trolltech com main snapshot qplaintext
  • 你最喜欢的 C++ 编码风格习语是什么 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何使用 use_library('django','1.2')

    我正在学习在 Google App Engine 中进行开发 这是教程中的代码之一 http code google com appengine docs python gettingstarted usingwebapp html htt
  • 如何防止在 twitter bootstrap typeahead 插件中按 Enter 键提交

    我有一个输入文本 我应用它的预输入插件来建议项目 但是当我在输入文本上按 Enter 键时 它会提交表单 如何使用 twitter bootstrap typeahead 插件阻止表单提交 您可以通过向特定输入添加 ID 并简单地删除 En
  • git flow 发布选定的功能

    我正在尝试向我的团队介绍 Git 流程 我们是一个相当小的团队 而且非常敏捷 我们希望每天发布一次 这意味着我们测试当天所有更改的时间有限 业务团队希望能够控制正在发布的功能 尽管这并不理想 Git 流程似乎不能很好地适应这一点 从开发中删
  • 在 Android 中调整图片大小同时仍保持质量

    我想将图像文件大小减小到 100 KB 以下 同时保持图像质量 就像 Whatsapp 和 Facebook 所做的那样 我尝试了 stackoverflow 上几乎所有可用的 android 图像压缩代码 但这对我不起作用 现在我正在关注
  • C++14 中的递归 lambda 函数 [重复]

    这个问题在这里已经有答案了 在 C 11 中编写递归 lambda 函数有一个经常重复的 技巧 如下所示 std function