Лямбда захватывает эту и длительную функцию

У меня есть функция, которая требует много времени для запуска, но, к счастью, она работает асинхронно. Я хочу взять результат этого вызова функции и установить его в частную переменную экземпляра класса. Кажется легким:

// Exists in some other library.
void LongRunningAsync(std::function<void(int)> callback) {
  sleep(10);
  callback(5);
}

class A {
 public:
  void Do() {
    auto lambda = [this](int val) {
      // Some processing...
      var_ = val;
    };
    LongRunningAsync(lambda);
  }

 private:
  var_;
};

int main() {
  A* a = new A;
  a->Do();
  // Wait for LongRunningAsync to finish.
  sleep(20);
  return 0;
}

Проблема заключается в добавлении следующей строки mainПрямо перед комментарием:

delete a;

Теперь когда LongRunningAsync вызывает обратный вызов, он попытается изменить переменную-член удаленного экземпляра (который является UB).

Есть ли способ спасти этот подход? Недавно я узнал о следующем решении:

void LongRunningAsync(std::function<void(int)> callback) {
  sleep(10);
  callback(5);
}

class A : public std::enable_shared_from_this<A> {
 public:
  void Do() {
    std::weak_ptr<A> weak = shared_from_this();
    auto lambda = [weak](int val) {
      auto shared = weak.lock();
      if (!shared) return;
      // Some processing...
      shared->var_ = val;
    };
    LongRunningAsync(lambda);
  }

 private:
  var_;
};

int main() {
  auto a = std::make_shared<A>();
  a->Do();
  // Wait for LongRunningAsync to finish.
  sleep(20);
  return 0;
}

Но это требует изменения всех A переменные для shared_ptr. Есть ли менее навязчивый способ сделать эту работу?

2 ответа

Одним из возможных решений является просто инкапсулировать нужное вам состояние в shared_ptr переменная-член, а затем захватывает это значение по замыканию, которое выполняется асинхронно.

Что-то вроде следующего

class A : public std::enable_shared_from_this<A> {
public:
  void Do() {
    auto lambda = [member_shared_state](int val) {
      member_shared_state->var_ = val;
    };
    LongRunningAsync(lambda);
  }
  ....
};

Вот решение, основанное на подходе Curious, но это не заставляет меня менять все указатели на A возражает против shared_ptr:

// Exists in some other library.
void LongRunningAsync(std::function<void(int)> callback) {
  sleep(10);
  callback(5);
}

class A {
 public:
  A() : var_(std::make_shared<int>()) {}
  void Do() {
    std::weak_ptr<int> weak = var_;
    auto lambda = [weak](int val) {
      auto shared = weak.lock();
      if (!shared) {
        return;
      }
      // Some processing...
      *shared = val;
    };
    LongRunningAsync(lambda);
  }

 private:
  std::shared_ptr<int> var_;
};

int main() {
  A* a = new A;
  a->Do();
  delete a;
  // Wait for LongRunningAsync to finish.
  sleep(20);
  return 0;
}
Другие вопросы по тегам