【问题标题】:Why is this recursive lambda function unsafe?为什么这个递归 lambda 函数不安全?
【发布时间】:2019-08-19 17:26:54
【问题描述】:

这个问题来自Can lambda functions be recursive?accepted answer 表示下面显示的递归 lambda 函数有效。

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

不过,a comment 指出

这样的函数不能安全返回

,原因在this comment中提供:

返回它会破坏局部变量,并且该函数具有对该局部变量的引用

我不明白原因。据我所知,捕获变量相当于将它们保留为数据成员(根据捕获列表按值或按引用)。那么在这种情况下什么是“局部变量”?此外,即使在 g++ 7.4.0 上使用 -Wall -Wextra -std=c++11 选项,下面的代码也能正常编译和工作。

#include <iostream>
#include <functional>

int main() {

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

    std::cout << factorial(5) << "\n";

}

为什么函数不安全?这个问题仅限于这个函数,还是整个 lambda 表达式?

【问题讨论】:

  • 我认为评论意味着你不能从函数中返回它。因此,如果您不能拥有std::function&lt;int (int)&gt; foo();,请返回您的factorialfactorial 将是 foo() 中的局部变量,并且您返回的函数对象将引用它。
  • this。它可以爆炸,它可以工作。我们只知道它是 UB。
  • C++ Returning reference to local variable可能重复(根本原因相同)
  • @ynn 您在问题中显示的示例具有明确定义的行为。

标签: c++


【解决方案1】:

这是因为为了递归,它使用类型擦除并通过引用捕获类型擦除的容器。

通过使用 std::function 间接引用它,这具有允许在其内部使用 lambda 的效果。

但是,要使其工作,它必须通过引用捕获std::function,并且该对象具有自动存储持续时间。

您的 lambda 包含对本地 std::function 的引用。即使您通过复制返回 std::function,lambda 仍将引用旧的,已死。

为了确保返回递归 lambda 的安全,您可以在 auto 参数中将 lambda 发送给自身,然后将其包装在另一个 lambda 中:

auto factorial = [](auto self, int i) -> int { 
    return (i == 1) ? 1 : i * self(self, i - 1); 
};

return [factorial](int i) { return factorial(factorial, i); };

【讨论】:

  • 得到了比我想要的更多的东西 :) 谢谢。
  • 提供的解决方案有一些小错误(并且无法编译),但我修复了它们
  • 您的解决方案不正确。它不编译,self 接受两个参数,而不是一个
  • @J.AntonioPerez 感谢您的指出,现在应该是正确的
猜你喜欢
  • 2020-10-03
  • 1970-01-01
  • 1970-01-01
  • 2021-10-28
  • 2013-09-23
  • 1970-01-01
相关资源
最近更新 更多