Despite some harsh words about Visual Studio 11, I’m finding that it makes my heart go pitter-pat every time I use it. Why? Because this early release is finally incorporating a decent set of long-awaited C++11 features. In this article I’ll show you how a little thing like a lambda can make a big difference in your coding style.
Microsoft and C++ - We Have History
Microsoft has a cyclic relationship with C++. In the early MFC days, the love was there big time - you had access to most of the system API using C++. However, around the turn of the millennium, Microsoft came under the Rasputin-like influence of Anders Hejlsberg and his beloved offspring, C#. Now it appears that maybe the pendulum is swinging back a bit, and C++ is no longer viewed as an afterthought. Great news.
Although Visual Studio 11 is a developer’s preview, Microsoft is saying that it is production ready - you can use this to create programs that are ready for release. In addition to touting a complete implementation of the C++11 standard library, an impressive list of language features have been turned on as well. (N.B. the path ahead is still long and arduous.)
Before even using Visual Studio C++ 11 to test a single line of code, I really appreciated reading Welcome Back to C++ (Modern C++), a manifesto that includes the following bullet points:
Modern C++ emphasizes:
- Stack-based scope instead of heap or static global scope.
- Auto type inference instead of explicit type names.
- Smart pointers instead of raw pointers.
- std::string and std::wstring types instead of raw char arrays.
- Standard template library (STL) containers — for example, vector, list, and map — instead of raw arrays or custom containers.
- STL algorithms instead of manually coded ones.
- Exceptions, to report and handle error conditions.
- Inline lambda functions instead of small functions implemented separately.
I feel that all of these changes result in safer code that is easier to read and maintain, without giving up the type-safety and efficiency that we love so much. Fully implementing these features either leans heavily on C++11 or requires it outright.
A Simple Example Using Naive C++98
It’s interesting to watch the evolution of code from C++ 98 to C++11 and see how it affects your code. You’ll see that the transformation can make it look like you are literally using a new programming language.
In this simple program, I’m taking a Scrabble rack of tiles and whipping through the Scrabble dictionary to find matches. Since it is a one-time call, I’m not storing the words, just doing a quick online comparison. In C++ 98, my code might have looked like this:
This works properly and I get what looks like correct output:
anoestri arsonite notaries notarise rosinate senorita
Classes Good, Templates Better
As people started to get more comfortable with templates and iterators, algorithms like this were commonly rewritten to take an range of iterators as input - much as the standard library algorithm functions do. This meant changing the function to a template function, but it did make it a lot more flexible. I could now call the function to operate on data from a file, just as before, but I can also now use any other container, or even an array as input:
More or less the same number of lines of code, but it is now generic.
Of course, just like with OOP, you need to take some care with template programming. Generic programming can’t be beat when it makes sense, but programmers have a particularly strong susceptibility to pro-innovation bias.
Using the Algorithms Library
Again, prodded by changing styles in the C++ world, my next step is to use a standard library algorithm to do the work. We’re told over and over that turning to the algorithms library allows you to use code that has been optimized to the n-th degree by the clever library teams.
In order to make this work, I have to call an algorithm with a predicate functor, seen below as
sorted_not_equal. Note also that I can’t use the logical function for this,
which would be
copy_if(). Why not? The committee forgot to put it in back in 1998,
2003, and 2005, a mistake that was fortunately remedied in C++11. So I have to use the inverse
remove_copy_if(), and invert the logical sense of my functor:
Functors Not So Hot
So this new approach is supposed to soup up my code by taking advantage of the algorithms that come with the standard library. But if you look around at the code people have been writing for the past 10 years, you’ll find that this style is pretty common in textbooks and magazine articles, but no so much in the real world.
Why not? Well, it’s pretty obvious. The generic algorithms in the library need lots of predicate glue to make them useful, and the work to create these predicates is just a pain. My code is almost twice as long, and the functionality that took two lines of code earlier is now bloated into a complete class definition. It pollutes my namespace, takes up a lot of space, and has to be defined somewhere distant from where it is actually used. Not a win.
This is obviously a problem when you look at the history of Linux, C, and C++. An entire family of technologies and infrastructure was developed with the implicit goal of reducing the number of keystrokes programmers had to enter. (I’m kidding, but only somewhat.) Functors are a step in the wrong direction.
Lambda to the Rescue
So it is with much relief that C++11 delivers lambdas, which allow us to write short sweet predicates exactly where we need them, as shown in this C++11 version of the example:
Yes, I now have to get used to a new syntax for writing lambda functions - I think that was unavoidable. But my lambda function is short, it is quite easy to see exactly what it is doing, and it replaces a gangly and awkward functor class.
Best of all, I use the lambda exactly where I need it - as the predicate parameter to an algorithm used in the standard library. Locality rules.
Visual C++ 11 provides a great framework for experimenting with lambdas, as they are supporting the 1.1 definition that was ratified as part of the standard. If you want the gory details, I believe the working group’s proposal has essentially the same wording that went into the standard. For a detailed tutorial, Herb Sutter’s talk can’t be beat.