About half a year ago we started compiling our code with -std=c++11
.
We had to fix a few, but not too many code parts for this. That was the easy part.
Getting C++11 to work on all supported platforms, build and testing environments was a bit more challenging, but we finally managed to do it.
Having used C++11 for some time now, I think it’s time to share a few of improvements in C++11 that solve common problems.
First of all, I don’t regret we changed to it. In my opinion, C++11 makes coding easier and safer. I will try to demonstrate that with a few examples in a second.
Before I go into details, just let me state that I will only show a few of my personal favorites here. There are so many more improvements in C++11 that are all worth having a look. If you haven’t looked into C++11 much, I recommend getting started at the Wikipedia page about C++11.
Auto
From a developer perspective, one of the most compelling features of
C++11 is the revamped auto
keyword. Consider the following C++98/C++03 code:
1 2 3 4 |
|
In C++11 this code can be simplified to:
1 2 3 4 |
|
In the C++11 version of the code, the compiler can figure out the type of
variable it
all by itself. This allows writing less (i.e. better) code.
The more complex the types are, the more helpful this gets. Compare the following two lines and check for yourself which one you prefer:
1
|
|
1
|
|
auto
provides an extra benefit:
when using auto
it is not necessary to repeat the type information
throughout the code. This is helpful when types need to be changed and the
change needs to be reflected everywhere. With auto
, chances are that less
code needs to be adjusted. And it is not necessary to set up extra
typedefs for this.
If you think auto
obfuscates the meaning too much, you can be a bit more
expressive, e.g.
1 2 3 4 |
|
Range-based loops
We all have written a lot of code that iterates over a ranges, like this:
1 2 3 4 |
|
C++11 provides a special range-based syntax for for
loops, which makes
this a lot easier and compact:
1 2 3 |
|
Decltype
As we have seen, the compiler can deduce the type of expressions
automatically. C++11 also allows using this type information with the
decltype
keyword. This allows to write more generic and maintainable code.
In the following code, the type of variable document
is a pointer to a
Document
. Variable myDocument
has the same type:
1 2 |
|
In C++03, we couldn’t tell the compiler that the two variables should
always have the same types. In C++11, we can explicitly give myDocument
the same type as document
, without any typedefs:
1 2 3 |
|
decltype
can also be used to deduce the type of expressions.
Lambdas / Closures
Lambdas are available in most other mainstream languages today, and they are available in C++11, too.
Probably one of the most common use cases for a lambda is a custom comparator function for sorting:
1 2 3 4 5 |
|
In the above example, the lambda has two input parameters and produces a boolean result. Note that the type of the result was not explicitly specified. Again the compiler is able to figure it out automatically.
A lambda can be assigned to a variable, and it can be passed as a parameter to another function/method. Lambdas can optionally have access to the variables of the scope they were created in.
The following code defines a struct ScopeGuard
that executes a lambda
in its constructor and another lambda in its destructor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
As mentioned before, decltype
can be used to determine the
return type of a function automatically. Here’s an example:
1 2 3 |
|
As can be seen in the examples above, C++11 has introduced an
alternative function declaration syntax, with the type of the
function result following a ->
. The return type can be omitted
if it can be unambiguously determined by the compiler. The new
function declaration syntax is mainly useful for lambdas, but
it can be used for regular functions, too.
Enum class
Enums in C++ are useful but just don’t feel right: persisting a struct that contains an enum value is not portable as the underlying data type for the enum is implementation-dependent.
Additionally, enum values can be compared to almost any other values, which in most cases doesn’t make sense but obfuscates coding errors. There were also scoping issues with enum values.
C++11 enum classes fix these problems. First of all, the underlying
data type for an enum can be specified. For example, this creates
an enum based with its value stored in an std::uint8_t
:
1 2 3 4 5 6 7 8 9 |
|
Regarding the comparison of enum values to other values, C++11
enum classes are much stronger typed than regular enums.
Comparing the status
variable from the above example to anything
but a value from its enum class won’t even compile.
This provides much greater type safety than when using the old, implicitly converting enums:
1 2 3 4 5 6 |
|
Finally, enum classes fix the scoping problems of regular enums. In C++03, the following code did not complile because two enums contained the same member name:
1 2 3 4 5 6 7 8 9 |
|
With C++11 enum classes, the following code is all fine:
1 2 3 4 5 6 7 8 9 |
|
Additional containers
C++11 provides the hash-based containers std::unordered_map
and std::unordered_set
(plus their non-unique counterparts).
These containers are not sorted, so they can be more efficient
than std::map
and std::set
.
Turning an std::map
into an std::unordered_map
is simple as
the APIs are more or less identical.
There is now also a singly-linked list container, named
std::forward_list
. This obviously allows forward iteration
only, but is more space efficient than the already existing
doubly-linked list container.
More
Other improvements include move semantics, atomic variables and operations, a dedicated type for NULL pointers, STL support for threads and mutexes, regular expressions, more Unicode support, override, final – to name only a few…