ttemple
ttemple

Reputation: 882

How to define a lambda function outside of a class and use it inside a class in C++?

I want to do something like this:

// define a lambda to be called from some class method
auto state_0_stuff = [&](){
    .
    caller_data.some_func();  <-- identifier undefined compiler error
    .
}

// call the lambda from some class method, capturing the data and using in the lambda.
void foo::some_func(){
    int state = 0;
    bool done = false;
    // more data...
    bar caller_data;

    while(!done){
        switch(state){
        case 0:
           state_0_stuff();       <-- instead of adding lots of code here, call lambda
           state = 1;
           break;
        case 1:
           done = true;
           break;
        }
    }
}

This will work if I define the lambda inside the body of foo::some_func, but not if I try to define the lambda outside.

Is there some relatively simple syntax to get this effect?

I don't care if lambdas are used, it was just my first idea. The intent is to simplify the contents of the switch(state) so that the code is more readable than if I add 50 lines of stuff in the case.

Obviously I could use normal functions, and pass a bunch of stuff back and forth, but it would be far less complex if the local state could just be captured by the function, as in Lambda's.

Upvotes: 2

Views: 3149

Answers (2)

NathanOliver
NathanOliver

Reputation: 180595

A lambda captures the state at the point it is declared. So when you have

auto state_0_stuff = [&](){
    lots_of_calcs_that_reference_caller_data();
    .
    caller_data.some_func();
    .
}

in the global space [&] will only capture what is available in that space. When you call the lambda in the function it will be using that captured state, not the current state of the function.

You are going to have to either declare the lambda in the function or write a function/functor that you pass the state to. You could also make it a private member function of the class so it will have access to the class state and you would only have to pass any function local state you need to it.


Some more depth on what happens when you declare a lambda:

When you do

auto foo = [&](){ your code here };

The compile expands it into something like (very over simplified):

struct some_compiler_generate_name
{
    some_compiler_generate_name(list of captured variables) : initialize captured variables {}
    operator () const { your code here  }
private:
    captured variables;
} foo(list of variables to capture);

So you can see that we create the lambda object right at the point of declaration and since that is done it gets initialized with any captured variables it might have. You can't recapture variables because you can't call its constructor again.

Upvotes: 4

Vittorio Romeo
Vittorio Romeo

Reputation: 93274

There is no way to implicitly capture objects in a lambda that's outside of the scope of those objects. You need to pass them explicitly.

As a solution, you can add a private member function to foo and call that instead:

void foo::case0(bar& caller_data, /* ... */) 
{
    lots_of_calcs_that_reference_caller_data();
    .
    caller_data.some_func();
    .
}

void foo::some_func(){
    int state = 0;
    bool done = false;
    // more data...
    bar caller_data;

    while(!done){
        switch(state){
        case 0:
           case0(caller_data, /* ... */);
           state = 1;
           break;
        case 1:
           done = true;
           break;
        }
    }
}

Otherwise you can create a struct with overloaded operator() and pass the "captures" in its constructor.

Upvotes: 4

Related Questions