3

I was reading a really great Bjarne Stroustrup's article where he exposes some C++ myths. I look at code that focus my attention, because I wouldn't know that C++ supports this kind of expressions. I'm talking about lambda expressions, or closures.

void do_my_sort(vector<double>& v)
{
  sort(v,[](double x, double y) { return x>y; });  // sort v in decreasing order
}

I was working with C# and JavaScript for several years, more recently with Java and Objective C and I find this language feature very powerful and useful. I currently doing some works in Smalltalk and, no surprise... the closures are there too...

So I was wondering, why this language feature came so late to C++ (2011)?

5 Answers5

17

C++ had function objects from 1983 onwards; they took/takes care of many examples where people now use lambdas (and use lambdas in other languages). In fact, a C++ lambda is probably best understood as a simplified notation for defining and immediately using a function object.

9

Weak compilers, large scope, existing library weaknesses, and potentially negative library interactions.

First, it was very hard for implementations to support all of C++98, and only occurred many years after the Standard shipped. Adding even more features in would only have made this worse. C# doesn't have this problem because C# 1.0/2.0 are very easy to implement in comparison. The fact is that even now C++ tooling is far behind tooling for these other languages because C++ is such a bitch to handle.

Secondly, C++ lambdas have to deal with a lot of additional details. For example, the lifetime of captured objects is something that C#/JS do not have to consider.

Thirdly, I will say that there were few examples of APIs where lambdas would have been genuinely useful. For example, the original STL did a lot with function objects. However, because you couldn't compose or lazy the algorithms, and iterators seriously suck, adding lambdas would not exactly have made them into LINQ. Really addressing these issues and making the C++ algorithms on par would have required a lot more work than just lambdas, a lot of which still hasn't been done. Compared to writing custom iterators and allocators of the time, and chaining together algorithms in-place on containers by hand, using a non-local struct functor was really not that bad.

Finally, it was unclear as to whether library techniques like expression templates could completely obviate the need for a language feature at all. It turns out that they can't, but there are other language features (non-placement new, delete and their array versions, native arrays) that are now not only completely redundant in C++11, but actively seriously harmful. Repeating these mistakes is undesirable. It's better to only resort to language features where you know for sure that a library can't handle it.

DeadMG
  • 36,914
6

What a closure does, in essence, is abstract over the lifetime of variables.

Manual memory management OTOH, requires the programmer to be aware of the lifetime of variables. But, how can you be aware of something that is abstracted away from you?

Closures are fundamentally incompatible with manual memory management. Reconciling the two is very hard, and the C++ community has done the best job they can, but actually, you can still get errors (Or maybe even UB? I admit I don't actually know C++.) using closures that capture variables which are then freed.

Or, put another way: closures extend the dynamic scope of variables beyond their lexical scope, but the fact that dynamic scope and lexical scope are identical is the fundamental basis of C++ idioms such as RAII.

C++ was AFAIK the first language with manual memory management to do closures, so they were doing original research and original research takes time. That's the main reason why it took so long. They were doing this research in a committee, which is about the worst place to do research, which probably prolonged the process even more, but the main reason is that they were doing hard original research, not that they were doing so in a committee.

Jörg W Mittag
  • 104,619
4

C++ did not have an ISO standard until well after it was first created. Much of its early life was fragmented, with compilers all working differently to the point of being source-incompatible at times.

The first C++ standard was C++98, with relatively minor updates in the C++03 standard. The primary goal of these standards was to take the "wild west" of C++ and bring some semblance of order to it. Once the dust settled and developers got over the changes, the C++ committee would focus on new features.

From Bjarne Stroustrup himself (some parenthesis removed for brevity), The C++ Programming Language (4th Edition), section 1.4.4 "The 2011 Standard":

The current C++, C++11, known for years as C++0x, is the work of the members of WG21. The committee worked under increasingly onerous self-imposed processes and procedures. These processes probably led to a better (and more rigorous) specification, but they also limited innovation. An initial draft standard for public review was produced in 2009. The second ISO C++ standard was ratified by a 21-0 national vote in August 2011.

One reason for the long gap between the two standards is that most members of the committee (including me) were under the mistaken impression that the ISO rules required a "waiting period" after a standard was issued before working on new features. Consequently, serious work on new language features did not start until 2002.

C++ was not even standardized until 1998, and the next few years were spent fixing errors in the existing (new) standard. Then the committee sat on its hands for a couple of years due to procedural issues that were likely unnecessary. It then took seven years from initial inception to a public document, with much arguing and editing along the way. Even then, it took another two years until the document was in a position to be voted on and ratified.

What this means is the addition of lambdas and other language features had to wait until the status quo was stabilized (1998/2003), then politics and procedures of the process dragged out the addition of new features far longer than it probably needed to (2011).


Another reason for the lack of urgency is that functors can do almost anything a lambda can do, they are just more verbose. The language as a whole was not missing much other than the brevity of lambdas and their ability to bind to local variables. However, even that can mostly be worked around by passing references into the functor.

0

What's the difference between a functor and a lambda? The way the code you write looks!

A lambda is just a functor written in-line so to avoid a bit of boring boilerplate (replacing it with less boilerplate). So asking why C++ didn't have this feature is missing the point of what the feature really is. What extra functionality do you gain from a lamdba that you didn't get from a functor?

So apart from a short-hand syntax, there's nothing new to see here. (and remember some shorthand syntax is bad, take a look at some perl sometime and tell me shortening language features so they're easy and quick to type is a good thing :-) )

gbjbaanb
  • 48,749
  • 7
  • 106
  • 173