The complicated part of this recipe is the following line:
const std::vector<std::function<void(int)>> consumers
{consumer(d), consumer(l), consumer(v)};
The objects d, l, and v are each wrapped into a consumer(...) call. This call returns function objects, which then each capture references to one of d, l, and v. Although these function objects all accept int values as parameters, the fact that they capture completely different variables also makes them completely different types. This is like trying to stuff variables of type A, B, and C into a vector, although these types have nothing in common.
In order to fix this, we need to find a common type, which can store very different function objects, that is, std::function. An std::function<void(int)> object can store any function object or traditional function, which accepts an integer parameter and returns nothing. It decouples its type from the underlying function object type, using polymorphy. Consider we write something like this:
std::function<void(int)> f (
[&vector](int x) { vector.push_back(x); });
Here, the function object which is constructed from the lambda expression is wrapped into an std::function object, and whenever we call f(123), this leads to a virtual function call, which is redirected to the actual function object inside it.
While storing function objects, std::function instances apply some intelligence. If we capture more and more variables in a lambda expression, it must grow larger. If its size is not too large, std::function can store it within itself. If the size of the stored function object is too large, std::function will allocate a chunk of memory on the heap and then store the large function object there. This does not affect the functionality of our code, but we should know about this because this can impact the performance of our code.