Как определить лямбда-функцию вне класса и использовать ее внутри класса в C ++? - PullRequest
0 голосов
/ 13 сентября 2018

Я хочу сделать что-то вроде этого:

// 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;
        }
    }
}

Это сработает, если я определю лямбду внутри тела foo :: some_func, но не если я попытаюсь определить лямбду снаружи.

Существует ли относительно простой синтаксис для получения этого эффекта?

Мне все равно, используются ли лямбды, это была только моя первая идея.Намерение состоит в том, чтобы упростить содержимое переключателя (состояния), чтобы код был более читабельным, чем если бы я добавил 50 строк материала в этом случае.

Очевидно, я мог бы использовать обычные функции и передать кучувещи взад и вперед, но было бы гораздо менее сложно, если бы локальное состояние могло быть захвачено функцией, как в лямбде.

Ответы [ 2 ]

0 голосов
/ 13 сентября 2018

Не существует способа неявного захвата объектов в лямбда-выражениях, находящихся вне области действия этих объектов.Вы должны передать их явно.

В качестве решения вы можете добавить private функцию-член к foo и вызвать ее вместо:

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;
        }
    }
}

В противном случае вы можете создать struct с перегруженным operator()и передать «захват» в его конструктор.

0 голосов
/ 13 сентября 2018

Лямбда фиксирует состояние в точке, в которой оно объявлено.Поэтому, когда у вас есть

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

в глобальном пространстве, [&] будет захватывать только то, что доступно в этом пространстве.Когда вы вызываете лямбду в функции, она будет использовать это захваченное состояние, а не текущее состояние функции.

Вам нужно будет либо объявить лямбда в функции, либо написать функцию / функтор, которыйВы передаете состояние в.Вы также можете сделать ее закрытой функцией-членом класса, чтобы у нее был доступ к состоянию класса, и вам нужно было бы только передать ему любое нужное локальное состояние функции.


Еще немного глубинычто происходит, когда вы объявляете лямбду:

Когда вы делаете

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

Компиляция расширяет его до чего-то вроде (очень упрощенно):

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);

Так что выМожно видеть, что мы создаем лямбда-объект прямо в точке объявления, и, поскольку это сделано, он инициализируется с любыми захваченными переменными, которые у него могут быть.Вы не можете перехватить переменные, потому что вы не можете снова вызвать их конструктор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...