I'm not a C++ programmer, but I was under the impression that closures in c++ were just classes that overload the function call operator `operator()`. So each closure could also be implemented as a named class. Something like:
Indeed, that is exactly the case, lambdas are essentially syntax sugar for doing this.
The one thing the author's solution does which this solution (and lambdas) does not is type erasure: if you want to pass that closure around, you have to use templates, and you can't store different lambdas in the same data structure even if they have the same signature.
You could solve that in your case by making `void operator()` virtual and inheriting (though that means you have to heap-allocate all your lambdas), or use `std::function<>`, which is a generic solution to this problem (which may or may not allocate, if the lambda is small enough, it's usually optimized to be stored inline).
I get where the author is coming from, but this seems very much like an inferior solution to just using `std::function<>`.
The author of the article freely admits that `std::function<>` is more flexible. He still prefers this solution, as it is easier for him to reason about. This is covered in the "Fringe Benefits" part of the document.
> though that means you have to heap-allocate all your lambdas
I think whether or not you have to allocate from the heap depends on the lifetime of the lambda. Virtual methods also work just fine on stack-allocated objects.
Fair point, but generally speaking, callbacks tend to escape the scopes they are in (if you have a callback for ”user clicked mouse”, it’s likely not going to be triggered in your current scope), so stack-allocation isn’t really an option.
But yes, fair point: they can be stack or statically allocated as well.
Exactly! And if you need type erasure, you can just store it in a std::function.
> OnListItemSelectedData data;
In this case you can just store the data as member variables. No need for defining an extra class just for the data.
As I've written elsewhere, you can also just use a lambda and forward the captures and arguments to a (member) function. Or if you're old-school, use std::bind.
Yes, but that's exactly why I mention this. By explicitly creating a class (that behaves the same as a lambda) the author might get better names in crash reports.