Note: This entry has been restored from old archives.
I just read Herb Sutter’s C++0x lambda/closure teaser post. It’s intriguing, although I cringe at the new syntax. I’ll be mulling over this somewhat and waiting to see what else is said about it by the Internet’s hoard of people more qualified than I… though, of course, Herb Sutter may be the most qualified out there! No doubt I’ll eventually be convinced the new features are wonderful. For now I’m going to hope the technical brilliance of the ISO C++ committee has given birth to something beautiful – and in this instance Herb Sutter hasn’t done a good job of marketing it to numpties like me. (Then again, exception specifications got into the standard as well and it seems that even the people responsible for them would like to disown them now.)
In the “Write collection to console” example I have trouble seeing how the lambda version is obviously better. I look at the three examples and think to myself “huh, what’s the point?” The fact is that the for loop is clear, the for_each
lambda version just makes it a little more terse. How about:
#include <boost/foreach.hpp> ... BOOST_FOREACH(const Widget & widget, w) { cout << widget << " "; }
More readable than all Herb’s examples? Possibly. Herb Sutter makes a note that the final lambda-using example is the only one he typed up correctly before doing a complier-run. Maybe this could be related to the fact he’s probably been steeped in the specification of the lambda features lately?
The “Find element with Weight() > 100” example is a better one, aside from the grotesqueness of the lambda syntax. This [](
has a face that only a mother could love:
find_if( w.begin(), w.end(), []( const Widget& w ) -> bool { w.Weight() > 100; } );
Herb dares us to have a go with binders, here’s what I knock up for boost::bind
:
std::find_if(w.begin(), w.end(), boost::bind(std::greater(), boost::bind(&Widget::Weight, _1), 100));
This is a typical “bind class member return value to function (functor) argument” usage, I see (and use) it a lot. Though for the “>” case there’s an even more terse solution:
std::find_if(w.begin(), w.end(), boost::bind(&Widget::Weight, _1) > 100);
I don’t particularly think one syntax is more readable or obvious than the other, but I’m used to reading uses of bind. Truth be told however, Herb’s functor example is easily the clearest at point-of-use. Is the clarity not worth the small clutter of having the GreaterThan
functor implementation lying around somewhere (and entirely re-usable.) Herb has perhaps not chosen the best example here. Something just a little more complex could make the lambda version seem a lot better than the bind version, though such complexity would just make the overhead of writing the functor less of a concern (and the non-reusable lambda code seem more of a bad idea.)
[[Note: Since I wrote the above, on Saturday, a lively discussion has popped up in the comments on Herb Sutter’s post. In there he explains two strong disadvantages of the binder approach: 1) recognisable function syntax is lost with binders, 2) errors in binders generate some of the most horrible C++ template-spew error messages you can get out of compliers. That latter part is certainly a pain, it takes a few weeks of working with, and making mistakes with, binders before you can weed useful information out of those errors. Though they’re usually down to just a couple of syntax mistakes and you can check for them first and correct without needing to decipher the errors in 80%, or more, of cases. The other common problem I find with boost::bind
is passing object arguments and forgetting boost::ref
, an error you’d be much less likely to make with the function-declaration style of the lambda syntax.]]
The final “Most algorithms are loops” example is more compelling. But again, the example used doesn’t seem a great win over boost::bind
. And both lambdas and binders seem a less maintainable route than functors. [[Note: See previous note for positive points of the lambda syntax over binders that also apply here.]]
It seems to me that the lambda syntax may offer a more robust and maintainable replacement for binders and, in extension, be a better way to “turn things into functors.” That is certainly a worthy achievement in itself. For simple cases the lambda syntax can provide an “it’s all in front of you” clarity to improve the readability of algorithms. For more complex cases I see it being used in lazy ways and giving the world some knotty code maintenance nightmares in the name of the quick hack. Stick to functors is my leaning at the moment, or use functions combined with the lambda syntax as a replacement for binders, if you must. It’s early days yet though, these are some first simple examples and the standard is still in progress. [[Note: insights from the “Beautiful Code” blog. Note that the non-standard solution to his .begin()/.end() pattern dislike is BOOST_FOREACH
, it’d be nice to see something like that go into the standard.]
It’s great to see non-trivial new features going into C++, I look forward to reading what the experts have to say about it all! It’s worth noting that the items mentioned by Herb are just the most buzz-word friendly ones, an act of marketing no doubt. I suggest exploring the “Other approved features” that he links to, there’s some interesting ones in there. I’m most immediately intrigued by N2555, N2559, N2540, and N2535 – unfortunately I don’t have time to consider them in detail.