@pixik

Для чего нужны замыкания в C++ и как вы их используете?

Добрый день! Не могу понять для чего нужны замыкания и почему с ними жить лучше чем без? Кто использует в реальных проектах, можете поделиться опытом?
  • Вопрос задан
  • 8684 просмотра
Решения вопроса 2
Замыкания неотделимы от лямбд. Они позволяют передавать в лямбду ссылки. Например в лямбде вам потребовалось менять приватную переменную класса. Без замыкания вам пришлось бы делать сеттер и геттер (что уже меняет интерфейс класса, их можно будет вызвать там, где не стоило бы), передавать в лямбду указатель на объект.
А так вы работаете с переменной объекта внутри лямбды, как будто это локальная переменная лямбды.

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

using IntV = vector<int>;

int main(int argc, char const *argv[]) {

  IntV v = {0, 9, 4, 6, 7, 8, 5, 6};
  size_t count = 0;

  for_each(begin(v), end(v), [](IntV::value_type x){cout << x << " ";});
  cout << endl;

  sort(begin(v), end(v), [&count /*closure*/](IntV::value_type &a, IntV::value_type &b){
    ++count;
    return a < b;
  });

  cout << count << endl;

  for_each(begin(v), end(v), [](IntV::value_type x){cout << x << " ";});
  cout << endl;

  return 0;
}


Вот пример вам подсчёта количества сравнений при сортировке вектора.
Ответ написан
@Mercury13
Программист на «си с крестами» и не только
Замыкание — это способ передать в callback, из какого контекста он запустился.
struct Row {
  int data[10];
};
struct Table {
  Row rows[10];
}

Нам нужно отсортировать таблицу по j-му столбцу. Номер j заранее неизвестен.

int sortJ;

int myCompare(const void* a,const void* b) {
  int ia = reinterpret_cast<Row*>(a)->data[sortJ];
  int ib = reinterpret_cast<Row*>(b)->data[sortJ];
  if (ia < ib) return -1;
  if (ia == ib) return 0;
  return 1;
}

int someJ = 5;
sortJ = someJ;
qsort(table.rows, 10, sizeof(Row), myCompare);

Вот эта переменная sortJ — по какому столбцу сортировать — это и есть замыкание. Но, как известно, «избегай незнакомых женщин и глобальных переменных». Поэтому на STL мы делаем функтор (объект-функцию) и эту информацию кидаем в него.

class MyCompare {
public:
  MyCompare(int aJ) : j(aJ) {}
  bool operator () (const Row& a, const Row& b) const
    { return (a.data[j] < b.data[j]); }
private:
  const int j;
}

int someJ = 5;
std::sort(table.rows, table.rows + 10, MyCompare(someJ));

Вот мы и избавились от глобальной переменной, закинув наше замыкание в private-поля объекта.

Что плохо? Не будем говорить про технические тонкости. С точки зрения красоты и лаконичности кода: код слишком разлапистый. И тут пришёл C++11.
int someJ = 5;
std::sort(table.rows, table.rows + 10,
  [someJ](const Row& a, const Row& b) -> bool { return (a.data[someJ] < b.data[someJ]); } );

Корявовато, но таков синтаксис Си++. Автоматически создаётся объект-функтор, и someJ становится его полем. Вот оно, замыкание — [someJ] — то есть те вещи, которые надо протащить внутрь функтора.

Из реального проекта. Отбегал поток автоматического поиска нового регистрационного ключа; если что-то получилось — синхронно вызываем лямбду через Qt’шный механизм «сигнал-слот». Чтобы всё было синхронно, нужен объект, живущий в главном потоке (и он в интерфейсе MainControl — управление главной формой — тоже есть). Но тогда придётся вызывать не слот, а лямбду. Этой лямбде нужны два поля: fReregKey (новый ключ защиты от копирования) и fMainControl. Оба они в this, его и замыкаем.
connect(this, &RenewThread::needUpdate, fMainControl.maincQobj(),
            [this]() {
        drm::createFile(fReregKey);
        fMainControl.maincLayoutOnRegister();
    });


А теперь посмотрим в WinApi. Первая попавшаяся функция из DirectInput.
HRESULT EnumObjects(
         LPDIENUMDEVICEOBJECTSCALLBACK lpCallback,
         LPVOID pvRef,
         DWORD dwFlags
);

BOOL DIEnumDeviceObjectsCallback(
         LPCDIDEVICEOBJECTINSTANCE lpddoi,
         LPVOID pvRef
);

Про pvRef говорится: функции EnumObjects он ни на что не нужен; что функция приняла, то и даст в callback. Тоже форма замыкания: можно передать указатель на любые данные, которые нужны callback’у.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы