首页 > 解决方案 > 为什么这个递归 lambda 函数不安全?

问题描述

这个问题来自Can lambda functions be recursive? . 接受的答案说下面显示的递归 lambda 函数有效。

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

但是,有评论指出,

这样的函数不能安全返回

,原因在此评论中提供:

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

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

#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 表达式?

标签: c++

解决方案


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

这具有允许在其内部使用 lambda 的效果,通过使用std::function.

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

您的 lambda 包含对本地的引用std::function。即使您返回std::functionby 副本,lambda 仍将引用已死的旧版本。

为了确保返回递归 lambda 的安全,您可以将 lambda 在auto参数中发送给自身并将其包装在另一个 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); };

推荐阅读