Clones1201
Clones1201

Reputation: 343

Why can't I capture a recursive lambda function in C++?

I find that if a lambda is a recursive function which is calling itself, then it can't be captured by another lambda as working in a closure in C++.

I have some codes like this:

#include <memory>
#include <functional>
#include <iostream>

class ClassA
{
public:
    std::function<void()> FuncA;
    void Call()
    {
        FuncA();
    }
};

class ClassB
{
    std::unique_ptr<ClassA> pA = std::make_unique<ClassA>();
public:
    void Setup()
    {
        std::function<void(int)> FuncB = [&](int a)
        {
            std::cout << "a = " << a << std::endl;
            if(a > 0)
                FuncB(a-1);
        };

        pA->FuncA = [&]()
        {
            FuncB(10.0f);
        };
    }
    void Run()
    {
        Setup();
        pA->Call();
    }   
};

int main() {

    ClassB B;
    B.Run();
}

a exception will occur when running to calling FuncA, because FuncB in it will be a empty pointer.

My question is why can't I capture a recursive lambda function?

I'm using Visual Studio 2015

EDIT: If capture FuncB by copy in FuncA, then it works if FuncB is not recursive. like this:

class ClassB
{
    std::unique_ptr<ClassA> pA = std::make_unique<ClassA>();
public:
    void Setup()
    {
        std::function<void(int)> FuncB = [FuncB](int a)
        {
            std::cout << "a = " << a << std::endl;
            if (a > 0)
                FuncB(a - 1);
        };

        pA->FuncA = [FuncB]()
        {
            FuncB(10.0f);
        };
    }
    void Run()
    {
        Setup();
        pA->Call();
    }
};

Upvotes: 2

Views: 304

Answers (2)

slashmais
slashmais

Reputation: 7155

Made small modifications to your code, and this works fine:

#include <iostream>
#include <functional>

struct A
{
    std::function<void()> fa;
    void call() { fa(); }
};

struct B
{
    A *pA;
    B() {pA=nullptr; }
    typedef std::function<void(int)> FB;
    FB fb;
    void Setup()
    {
        fb=[&](int i)
        {
            std::cout << "i = " << i << "\n";
            if(i > 0) fb(i-1);
        };
        if (pA) { pA->fa=[&]() { fb(10.0f); }; }
    }
    void Run(A*p)
    {
        pA=p;
        Setup();
        pA->call();
    }
};

int main()
{
    A a;
    B b;
    b.Run(&a);
    return 0;
}

Maybe this will help you refine your algorithm.

Upvotes: 1

Chris Dodd
Chris Dodd

Reputation: 126418

You're capturing FuncB by reference, but FuncB is destroyed when Setup returns, leaving you with a dangling reference.

If you change FuncB to be captured by value, then the first lambda captures it before it is initialized, which leads to undefined behavior.

I can't think of any way to get a lambda to capture itself, like you are trying to do.

Upvotes: 3

Related Questions