Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this recursive lambda function unsafe?

Tags:

c++

This question comes from Can lambda functions be recursive? . The accepted answer says the recursive lambda function shown below works.

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.

#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?

like image 774
ynn Avatar asked Aug 19 '19 17:08

ynn


People also ask

Can a lambda function be recursive?

You may not realise that you can write AWS Lambda functions in a recursive manner to perform long-running tasks. Here's two tips to help you do it right. AWS Lambda limits the maximum execution time of a single invocation to 5 minutes.

What is recursive lambda?

A recursive lambda expression is the process in which a function calls itself directly or indirectly is called recursion and the corresponding function is called a recursive function. Using a recursive algorithm, certain problems can be solved quite easily.

How do you stop a recursive lambda?

If you trigger a recursive invocation loop accidentally, you can press the “Throttle” button in the Lambda console to scale the function concurrency down to zero and break the recursion cycle.

How do you recursively call lambda?

The recipe is simple: If you want to call a lambda recursively, just add an auto&& parameter taking the function again and call that. This produces basically optimal assembly and can be used in combination with capturing.


Video Answer


1 Answers

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.

However, for it to work, it must capture the std::function by reference, and that object has automatic storage duration.

Your lambda contains a reference to a local std::function. Even if you return the std::function by copy, the lambda will still refer to the old one, that died.

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:

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

return [factorial](int i) { return factorial(factorial, i); };
like image 53
Guillaume Racicot Avatar answered Dec 01 '22 11:12

Guillaume Racicot