The basic problem that we're trying to solve is that some algorithm expects a function that only takes a particular set of arguments (one int
in your example). However, we want the function to be able to manipulate or inspect some other object, maybe like so:
void what_we_want(int n, std::set<int> const & conditions, int & total)
{
if (conditions.find(n) != conditions.end()) { total += n; }
}
However, all we are allowed to give our algorithm is a function like void f(int)
. So where do we put the other data?
You could either keep the other data in a global variable, or you could follow the traditional C++ approach and write a functor:
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;
};
Now we can call the algorithm with a suitably initialized functor:
std::set<int> conditions;
int total;
for_each(v.begin(), v.end(), what_we_must_write(conditions, total));
Finally, a closure object (which is described by a lambda expression) is just that: A short-hand way of writing a functor. The equivalent of the above functor is the lambda
auto what_we_get = [&conditions, &total](int n) -> void {
if (condiditons.find(n) != conditions.end()) { total += n; } };
The short-hand capture lists [=]
and [&]
just capture "everything" (respectively by value or by reference), which means that the compiler figures out the concrete capture list for you (it doesn't actually put everything into the closure object, but only the things you need).
So, in nutshell: A closure object without capture is like a free function, and a closure with capture is like a functor object with suitably defined and initialized private member objects.