简体   繁体   English

为什么这个递归lambda函数不安全?

[英]Why is this recursive lambda function unsafe?

This question comes from Can lambda functions be recursive? 这个问题来自Can lambda函数是递归的吗? . The accepted answer says the recursive lambda function shown below works. 接受的答案说下面显示的递归lambda函数有效。

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

However, it is pointed out by a comment that 但是, 评论指出了这一点

such a function cannot be returned safely 这样的功能无法安全返回

, and the reason is supplied in this comment : ,原因在此评论中提供

returning it destroys the local variable, and the function has a reference to that local variable . 返回它会破坏局部变量,并且该函数具有对该局部变量的引用

I don't understand the reason. 我不明白原因。 As far as I know, capturing variables is equivalent to retaining them as data members (by-value or by-reference according to the capture list). 据我所知,捕获变量等同于将它们保留为数据成员(根据捕获列表按值或按引用)。 So what is "local variable" in this context? 那么在这种背景下什么是“局部变量”呢? Also, the code below compiles and works correctly even with -Wall -Wextra -std=c++11 option on g++ 7.4.0. 此外,即使在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";

}

Why is the function unsafe? 为什么功能不安全? Is this problem limited to this function, or lambda expression as a whole? 这个问题仅限于此函数,还是整个lambda表达式?

This is because in order to be recursive, it uses type erasure and captures the type erased container by reference. 这是因为为了递归,它使用类型擦除并通过引用捕获类型擦除容器。

This has the effect of allowing to use the lambda inside itself, by refering to it indirectly using the std::function . 这具有允许在内部使用lambda的效果,通过使用std::function间接引用它。

However, for it to work, it must capture the std::function by reference, and that object has automatic storage duration. 但是,要使其工作,它必须通过引用捕获std::function ,并且该对象具有自动存储持续时间。

Your lambda contains a reference to a local std::function . 您的lambda包含对本地std::function的引用。 Even if you return the std::function by copy, the lambda will still refer to the old one, that died. 即使你通过副本返回std::function ,lambda仍将引用旧的,死亡。

To make a secure to return recursive lambda, you can send the lambda to itself in an auto parameter and wrap that in another 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); };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM