Как лямбда может захватить себя для асинхронного вызова? - PullRequest
3 голосов
/ 29 мая 2019

Я должен сделать асинхронный вызов внутри лямбды, и как только асинхронный вызов завершится, я должен вызвать саму лямбду.

Я пытаюсь объяснить мою проблему с помощью кода:

typedef function<void(int id)> Callback;
AsyncWork1(Callback call, int id, string)
{
    //...
    call(id);
}

AsyncWork2(Callback call, int id, double, string)
{
    //...
    call(id);
}
void AsyncWorks(Callback final_callback, int id)
{
    Callback lambda = [&lambda, final_callback, id](int next_work) -> void
        {
            if(next_work == 1)
            {
                //...
                AsyncWork1(lambda, 2, "bla bla");
            }
            else if(next_work == 2)
            {
                //...
                //the lambda variable no longer exists
                AsyncWork2(lambda, 3, 0.0, "bla bla");
            }
            else if(next_work == 3)
            {
                //...
                final_callback(id);
            }
        };

    lambda(1);
}
int main()
{
    AsyncWorks(...);

    AsyncWorks(...);

    AsyncWorks(...);

    AsyncWorks(...);

    return 0;
}

Проблема в том, что когда код выходит из функции "AsyncWorks (...)", локальная переменная "lambda" больше не существует.

У меня естьПрочитайте несколько веток, в которых рассказывается о рекурсивной лямбде, но я не нашел никакого решения.

Как я могу решить эту проблему?

Ответы [ 2 ]

2 голосов
/ 29 мая 2019

Основная проблема заключается в том, что C ++ не предоставляет самому себе указатель this лямбды.

Как это бывает, есть много языков, где во время определения чего-либо вы не можете ссылаться на себя. Это исправлено в функциональных языках с использованием техники, называемой «Y Combinator».

Простой Y-комбинатор в C ++ выглядит так:

template<class F>
struct y_combinator_t {
  F f;
  template<class...Args>
  auto operator()(Args&&...args)
  -> std::result_of_t< F&( y_combinator_t<F>&, Args&&... ) >
  {
    return f( *this, std::forward<Args>(args)... );
  }
};
template<class F>
y_combinator_t<std::decay_t<F>> y_combinate( F&& f ) {
  return {std::forward<F>(f)};
}

У меня два мнения, если мы должны f( *this или f( f, я иногда делаю либо.

Использование:

void AsyncWorks(Callback final_callback, int id)
{
  Callback lambda = y_combinate(
    [final_callback, id]
    (auto& self, int next_work)
    -> void
    {
      if(next_work == 1) {
        //...
        AsyncWork1(self, 2, "bla bla");
      } else if(next_work == 2) {
        //...
        //the lambda variable no longer exists
        AsyncWork2(self, 3, 0.0, "bla bla");
      } else if(next_work == 3) {
        //...
        final_callback(id);
      }
    }
  );
  lambda(1);
}

В основном я добавил неявный параметр self в тело лямбда-функции. Абонент operator() не видит этот параметр.

Y комбинатор на основе этот пост сам с изменениями.

0 голосов
/ 29 мая 2019

Лямбда может захватывать себя неявно. Для демонстрации, как? см. ниже код это рассчитать факториальное значение.

#include <iostream>

int (* factorial)( const int) = []( const int number)
{
    if( number > 1)
    {
        return number* factorial( number - 1);
    }
    else
    {
        return 1;
    }
};

int main(int , char *[])
{
    int fact = factorial( 7);
    std::cout<< "7! = "<< fact<< std::endl;
}
Output 7! = 5040

Если в лямбде используется какая-либо переменная, то лямбда захватывает ее неявно, если она не фиксируется явно. Из-за этого внутри лямбды, имя factorial, ссылающееся на себя, доступно.
Но если вместо
int (* factorial)( const int) = []( const int number){//implementation };

используется auto, как показано ниже,

auto factorial = []( const int number){ //implementation };

тогда Компилятор g ++ выдает следующую ошибку:
error: use of ‘factorial’ before deduction of ‘auto’ return number* factorial( number - 1);

Это потому, что тип factorial не выводится, auto не выводит для того же блока управления. Имя factorial будет доступно только ниже декларации в случае auto.

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