2

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();
    }
};
2
  • 3
    You're capturing by reference and funcB no longer exists after Setup finished so yeah that's not going to end well. Commented Jan 23, 2016 at 5:36
  • change to capturing by copy isn't help... Commented Jan 23, 2016 at 6:09

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

You can make a lambda capture itself by having it capture a shared pointer to a unique pointer to itself. First you make a shared pointer to a null unique pointer, then you define the recursive lambda that captures the shared pointer (containing a blank pointer) and assign it into the unique pointer in the shared pointer it contains. The unique pointer assures that it's properly deleted.
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.