Замыкание — это способ передать в 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’у.