With single instructions taking less than a nanosecond to execute, and data chugging its way over the Internet with latencies in the dozens and hundreds of milliseconds, there’s ample room in the middle for a productivity-enhancing, language-generalizing, safety-increasing virtual machine. Except when there isn’t, as when pushing a huge dataset through a mathematical transformation, or when the abstraction provided by the VM or language allocates memory that is not strictly needed by the domain logic.
Apple has proved the popular appeal of rapid OS evolution (particularly in the mobile market), and new operating system capabilities are not generally available to the managed platforms. The JVM is particularly troubled by lowest-common-denominator capabilities, but Microsoft too has failed to deliver on its decade-old promise to make the CLR an equal, if not the primary, interface to Windows. Last year’s panic about Microsoft “abandoning .NET” proved to be overblown (an overreaction to “what was not said” at a few presentations), but it is absolutely true that Microsoft is emphasizing C++ development more prominently than it has in a decade.
In addition, while there’s plenty of time in the middle ground between clock speeds, network latencies and user-perceivable performance, the “put a VM in it” model introduces a significant energy penalty, since most VM instructions translate into multiple native instructions. Anyone who’s looked at a teardown of a tablet or a small notebook knows the astonishing volume dedicated to the battery, and battery life is the most important feature for smartphone buyers.
Finally, although C and C++ get little respect from the academic language community, it is hard to ignore the fact that they remain at or near the very top of languages mentioned in help wanted ads: knowing C and C++ remains one of the very most valuable skills to have in your portfolio.
There are two major criticisms of C and C++: that the languages are not highly productive for application development, and that they are too complex. The latter is often made more emphatic by the complaint that the two languages are “designed by committee.” Both critiques have a good dose of truth. C and C++ have traditionally required significant effort to manage memory, bugs have a tendency to be more opaque than in managed languages, and the edit-run-debug cycle can be very slow in larger codebases.
And for complexity? Well, yeah. C++’s template facility is Turing complete and high-performing, which has led to the no-doubt complex technique of “template metaprogramming.” And both languages show the strata of the many epochs of programming during which they’ve evolved: integer types of ever-increasing sizes, archaic keywords like register and inline (I know, they’re not archaic everywhere), and perhaps above all the evolution of character representations and the string libraries, which have had to be improved to deal with both Unicode and buffer-overflow vulnerabilities.
Having said all that, one of the most interesting things about C++11 is that “making C++ easier to teach and learn” was one of the two leading goals, according to language designer Bjarne Stroustrup. The other was to “make C++ a better language for systems programming and library building.”
#!
‘More than a billion lines of code’
Stroustrup and others worked within the bureaucratic process of ISO standardization. The C and C++ languages are standardized under the International Organization for Standardization (ISO), the gold standard of technical standards groups.
The C and C++ ISO standards groups have been working since the mid-1990s. ANSI, the American National Standards Institute and the United States representative to ISO, worked on a C-language standard all the way back to the early 1980s. C has had three standards: C89, C99 and C11. (One often hears of the 1989 and 1999 standards called “ANSI C,” which is an anachronism now that the language is under ISO.) C++ has also had three: C++98, C++03 and C++11, as well as a library-focused “Technical Report” release in 2005.
The overwhelming theme of the C/C++ standards is that they’re conservative. C, in particular, is universal: If there’s a chip, there’s a C compiler for it. Every aspect of the language that has to do with memory or implies something about hardware implementation is going to be carefully scrutinized by literally hundreds of companies worrying, “What about me?”
And there’s a lot of C and C++ code out there. Coverity, which makes a static analysis tool, in 2009 had approximately 700 customers “with somewhat more than a billion lines of code among them.” (The quote is from the article “A Few Billion Lines of Code Later,” which is an absolute must-read for anyone interested in understanding the C and C++ world.) While Coverity is a successful and well-regarded company, there’s no way its customer base represents more than a tiny fraction of the total amount of C and C++ code used and depended upon throughout the world. So every change, no matter how trivial and obviously “correct” (for instance, not requiring a space between the closing right brackets in template expressions) is going to break someone’s code, somewhere, and cause pain. The awareness of how troublesome changes can be is palpable during discussions at standards meetings. That anything is approved can seem to an onlooker to be the biggest triumph of such meetings.
Language directives
But big changes have come to the languages, albeit slowly. C11 boasts better Unicode support, more control of alignment, and, most importantly, an improved and standardized memory model that supports thread-local storage and uninterruptible object access (a.k.a. “atomic” access, the guarantee that an access will not be interleaved with access from some other thread). The Unicode support, where a “u8”, “u” or “U” prefix on a string literal specifies the encoding, will probably be the most visible giveaway in C11 code.
The changes in C++ are considerably more visible. In addition to the directive of “making C++ easier to teach and learn” mentioned earlier, the other major directive was to “make C++ a better language for systems programming and library building.” Together, these two directives pushed the language to adopt a lot of lessons from the mainstream managed languages while never sacrificing the emphasis on performance or ability for low-level control.
#!
C++ changes
Type inference: Nowhere are the lessons of the mainstream languages clearer than in the adoption in C++ of type inference. The clearest visible signature of C++11 code will be the use of the auto keyword on the left-hand side of assignments. While this may feel a bit like the dynamic typing of a Python or Ruby, it’s not: The type is fixed and statically determined. The auto is just a convenience to avoid cumbersome “finger-typing” that often just restates what you’ve typed on the right-hand side (auto instead of, e.g., map<int,list<string>>::iterator or function<void(void)>). This “type inference” will be familiar to users of C# or Scala but will take some getting used to by others (especially when the compiler implementation is unable to infer the correct type and complains about it. Such complaints are a common source of questions on Stack Overflow for type-inferred languages).
Lambdas: Type inference saves a good amount of space when it comes to complex parameterized types, but is also an important enabler of what’s probably the biggest functional improvement to C++11, which is support for lambda functions.
Anonymous inline functions are again familiar to users of some of the more well-designed managed languages, but are even more universal in the world of JavaScript. Such “lambda” functions are wildly useful, particularly when used with Standard Template Library higher-order functions such as find_if. The syntax is a little more cumbersome than what one sees in other languages, as C++ requires you to specify the “captures” when the lambda function is being used as a closure. For example, [foo &bar](bat) -> int { …function body…} captures foo by value, bar by reference, takes bat as a parameter, and returns an integer (the return type can be elided in most cases). While the flexibility for different capture semantics is very nice, one can imagine stumbling the first several times one encounters that type of code.
Range-based for: The third highly visible feature in C++11 is “range-based for.” This compact way of specifying a loop can work with any type that supports the concept of a “range” (essentially, any type that has functions begin and end that yield iterators). All containers, arrays, initializer lists and regex matches can be looped on with a simple for(auto p : ps). This provides remarkable savings over the long type signatures of C++, to which developers using the standard containers have become accustomed.
What’s more likely to change the experience of programming is the stylistic imperative to “always use smart pointers or non-owning raw pointers.” Smart pointers in C++11 use the language’s template mechanism to provide reference-counted memory management, a type of garbage collection that is not as foolproof as the fully automatic garbage collection of managed languages, but which is much easier than fully manual memory management and is compatible with powerful C++ idioms such as Resource Acquisition Is Initialization (RAII).
C++98 had a flawed implementation of reference counting using the auto_ptr type. This type is now deprecated; instead, one uses unique_ptr for the owning relationship, and weak_ptr and shared_ptr for cycles and shared ownership. The great challenge with reference counting is that if you create a cycle in the object graph, the memory will never be freed; in C++11, the programmer is still responsible to correctly build the ownership graph.
Concurrency: C and C++ were invented in the era of single-processor machines, but are still the languages of operating-system implementations for the foreseeable future. As mentioned previously, the C11 memory model has been improved to be multi-thread safe, and C++ builds on that with a number of primarily library-based features.
Now, std::thread runs any callable object and runs it asynchronously. More interesting are the techniques associated with futures, which provide a straightforward conceptual model. (A Future is an IOU for a value; calling get on it blocks until the value is ready.) All the usual caveats (and flashing red lights and dire warnings nailed to doors in the dead of night) about the pitfalls of references and pointers and object lifetimes continue to apply.
The standard additionally specifies several types of Mutex, thread-local data, and thread-safe initialization. It is by no means a silver bullet for concurrency, but instead is an attempt to be a solid foundation for further work.
#!
In addition to the previously mentioned headline changes, there are a number of medium-impact features. At the border of the two are “move semantics,” which allow ownership of a structure to be changed rather than copied and then deleted; a potentially big performance win with large structures, but one from which you can only benefit by writing new code.
Consistent initialization using braces (int x{5}; int y[]{1, 2}) is another “easier to learn” feature, even if the semantics are slightly different for “aggregates” (arrays and structs) than they are for non-aggregates, and one is allowed to use fewer brace-initializers than needed, triggering value-construction (and, I think, making for pretty confusing code).
The new nullptr keyword is probably self-explanatory: It replaces 0 or NULL with a constant of type nullptr_t that can only be converted to a pointer or a Boolean (never, ever, to an int. Hooray!).
There are additional changes that I think of as minor fixes, such as the previously mentioned “right brackets close properly” fix.
In addition to the library-based threading and futures discussed previously, the new standard library includes hash tables, improved pseudo-random number functions, and regular expressions (regex adherence is said to be to “modified ECMAScript,” which, according to Scott Meyers’ “An Overview of the New C++” presentation materials, is “essentially a standardized version of Perl RE syntax”).
Still a work in progress
While the new C and C++ language standards are extensive and impressive, it’s fair to point out that C and C++ remain languages in which programmers can perform “dangerous” memory and thread operations: high-performance, low-level systems engineering requires that access. The hope, though, is that the new features will go a long way to avoiding the “slip of the mind” defects that, regrettably, we all introduce.
A major feature—some would say the major feature—of this version of C++ was a feature for templates called “Concepts.” It was pulled from the standard in 2009 because a majority of committee members felt that it was still untried and risky. Although a great deal of work has been done on Concepts over the years, it seems at this point that the feature may never make it into the standard or, if it does, only after a massive reworking.
Work has already begun on the next version of the standard. Aside from Concepts, major features will certainly include further steps toward a standard garbage collection model and some form of runtime reflection.
The improved memory model, reference-counting, and more concise code enabled by type inference and lambdas make the language more attractive and modern. Systems-level and performance-oriented programmers will certainly gain from enabling the latest language extensions in their compilers. The page seems a pretty actively maintained reference for compiler support.
Books on C/C++11 are mostly still in the development pipeline. An important exception is Anthony Williams’ excellent “C++ Concurrency in Action,” which goes into the memory model and related areas in great depth. The best general overview may be the course notes of Scott Meyers, author of the “Effective C++” series.
If C and C++ are undergoing a renaissance, it can only continue if the languages trigger enthusiasm in both new and old developers. The committees have done their work, now it’s time for the market to render judgment.