Основная проблема, которую мы пытаемся решить, заключается в том, что некоторый алгоритм ожидает функцию, которая принимает только определенный набор аргументов (один int
в вашем примере). Однако мы хотим, чтобы функция могла манипулировать или проверять какой-либо другой объект, например, так:
void what_we_want(int n, std::set<int> const & conditions, int & total)
{
if (conditions.find(n) != conditions.end()) { total += n; }
}
Однако все, что мы можем дать нашему алгоритму, это функция типа void f(int)
. Так, где мы помещаем другие данные?
Вы можете оставить другие данные в глобальной переменной или следовать традиционному подходу C ++ и написать функтор:
struct what_we_must_write
{
what_we_must_write(std::set<int> const & s, int & t)
: conditions(s), total(t)
{ }
void operator()(int n)
{
if (conditions.find(n) != conditions.end()) { total += n; }
}
private:
std::set<int> const & conditions;
int & total;
};
Теперь мы можем вызвать алгоритм с соответствующим образом инициализированным функтором:
std::set<int> conditions;
int total;
for_each(v.begin(), v.end(), what_we_must_write(conditions, total));
Наконец, закрывающий объект (который описывается лямбда-выражением ) - это просто: краткий способ написания функтора. Эквивалентом приведенного выше функтора является лямбда
auto what_we_get = [&conditions, &total](int n) -> void {
if (condiditons.find(n) != conditions.end()) { total += n; } };
Сокращенные списки захвата [=]
и [&]
просто захватывают «все» (соответственно по значению или по ссылке), что означает, что компилятор вычисляет конкретный список захвата для вас (он на самом деле не помещает все в объект замыкания, но только то, что вам нужно).
Итак, в двух словах: объект замыкания без захвата похож на свободную функцию, а замыкание с захватом похоже на объект функтор с соответствующим образом определенными и инициализированными объектами закрытого члена.