Not Your Father's C++
C, the workhorse language of the programming industry and technical academia for decades, is the progenitor of a family of some of the mostly widely used object-oriented languages in the world—most notably, C++, Java, and C#. C++ has been leap-frogged many times in terms of feature-set by Java and C#, which see major updates every few years. However, C++ is not owned by a single company so the gestation process for each major release takes quite a bit longer. Representatives from more than one-hundred companies combine to discuss, design, and vet new features and deprecate old ones, so we see a new version every decade or so.
The latest version of standard C++ was approved just last year, as its official title, C++11, reveals. This language not only catches up with other modern languages but it also leaps ahead of them with a few killer virtues of its own. Here's a quick list of notable changes:
- Range-based for loops (no need for a numeric index into sequences)
- Uniform initialization syntax (one way for everything)
- Type inference in variable declarations
- Lambda expressions (anonymous functions, closures)
- Static (compile-time) assertions
- Range-checked arrays (no space overhead beyond built-in arrays)
- Intelligent concurrency (lock-free and lock-based)
- Regular expressions (a la ECMAScript)
- Move semantics
The following snippet illustrates the new initialization syntax and type inference with auto:
Curly braces can be used for all initializations, even simple ones like
int x{7};
In the first code snippet, the compiler matches the initializers with the components of each map element in the intuitive way. The auto keyword causes p to have the appropriate element type for the map m (a pair of strings) and traversing the map is implicit.
The killer feature of C++11 is unquestionably the introduction of rvalue references, which enable "move semantics," among other things. Moving a value avoids making copies, such as with traditional pass-by-value (and even many uses of pass-by-reference), and greatly speeds up program execution when temporary values are passed as function arguments (which is all the time). The idea is to "steal" the contents of the temporary by swapping what the receiving parameter holds (often nothing or garbage) with the contents of the temporary. Such stealing usually involves swapping only pointers instead of copying all of an object's values.
Rvalue references are mainly intended for authors and users of class libraries, especially collection libraries, such as those provided by standard C++ itself. An rvalue reference parameter only matches a passed temporary value. Temporaries are anonymous and are only used once, so there is no harm in just "moving" their contents into the receiving parameter. You might think that just passing by reference solves this problem, but consider how vector's assignment operator is implemented in C++11:
-The parameter declaration identifies rhs to be an rvalue reference, so this function will only be called when assigning a temporary vector to the current object, as in
v = get_vector();
The temporary vector returned by get_vector() will not be copied at all. Instead, its contents will be swapped (i.e., “moved”) into v, simply by interchanging the internal pointers in the respective vector objects. The old C++98 way using a const reference parameter requires allocating new memory, deleting the old memory, and copying the incoming contents into the current vector. All containers in C++11 have been rewritten to accommodate move semantics. Even if you never have to use rvalue references in your own code, you get a tremendous speedup by just using the new C++11 containers.