September 30, 2015 - Daniel Kolesa

4 More Reasons You Should Use C++11

This article is part 3 of a 4 part series on the benefits of the C++11 revision of the C++ programming language

C++11 is the first update to the standard since 2003 and brings many significant features into the language. This article covers 4 more features that are significant: lambda expressions, expression SFINAE, rvalue references and move semantics, and constexpr.

Lambda expressions allow you to use functions in expression contexts. C++ also integrates basic closure functionality to share data between nested lambdas, allowing for cleaner functional style programming. However, as it has no garbage collection, effortless resource management with escaping closures is not possible (there are ways such as reference counting though). Expression SFINAE extends the original SFINAE rule, allowing for much cleaner template metaprogramming. Move semantics allow for more efficient and safer management of resources. Finally, constexpr extends certain parts of the language to compile time, allowing for various compile-time computations as well as proper constants. Each of these concepts will be covered in greater detail with this post.

Lambda Expressions

C++11 finally supports lambda expressions (anonymous functions). The basic syntax looks like this:

Lambdas will mostly figure out the return type themselves, but they can also be explicitly specified after the argument list using the trailing return type syntax.

The [] is a capture list. Lambdas can also be closures, albeit just simple dynamic closures since they won’t hold the lifetime of references they capture – so be careful. It’s possible to capture either individual local variables or all of them; it’s also possible to capture this:

Values captured by copy are considered immutable by default. It’s simply required to put the “mutable” keyword after the argument list if this behavior is undesirable.

Obviously, you will sometimes need to store a closure that captures some external resources. You will have to heap allocate those resources. Depending on complexity of your code (particularly, how many functions have access to said data) you might have to use reference counting to correctly manage the lifetime of the data. In other cases, simply moving the data into the closure will do.

Expression SFINAE

SFINAE stands for “Substitution Failure Is Not An Error” and it means that when a template argument fails substitution, it is not considered an error and instead the next viable option is tried. Since version 11, C++ has supported SFINAE on expressions. This is very useful, and I’ll explain why. One example is checking to see if a method with a required signature exists in a class.

There is a seemingly obvious approach that involves classic SFINAE. For example consider that we need to check for a method called foo that is constant and takes an int argument (and returns void). This would be a classic SFINAE solution:

It would then be used like so:

This seems like a sound solution, doesn’t it? However, it has one major caveat: It does not respect inheritance. For any methods declared in a parent class, you’re out of luck. Additionally, if it takes a slightly modified type, such as a const reference to the type that is normally fully compatible, it will not match.

In this case, we’re testing if a substitution fails when retrieving foo from the generic argument U. If it doesn’t fail, the return type of test will be char, otherwise it will be int. Those two have different sizes, so all that remains is to test the size of the return type to check which version was used.

Let’s try to figure out a solution now. You could possibly write a much more complicated SFINAE based algorithm that will cover your use case. But there’s no reason to; you can simply use Expression SFINAE for the purpose and save yourself time and effort. Let’s take a look at a rewrite of this test now:

Then it’s used identically:

This code is shorter and doesn’t suffer from the problem of the original version.At first, its way of doing this might be a bit unclear. Just like the original test, there are two function prototypes. One takes an int and one takes any type; we do this to separate the two function prototypes (so that they don’t conflict). The argument type does not really matter in this case.

SFINAE is now performed on the expression in the decltype(). We use std::declval to build an expression that we don’t want to fail. If it fails, the second variant is used. We also make the decltype() so that it expands to either std::true_type or std::false_type. For the second variant (which is used when the first variant fails substitution), it’s always std::false_type. For the first variant, it depends on the test; a call is issued and the type is tested to see if is void.

Unlike the original version, this will respect inheritance, because it follows the same flow as any C++ expression and has the same rules. In regular C++, it is possible to call methods from a parent class, so there is no reason for this to not work here. It also simplifies the core greatly and allows for writing of very complicated tests that would take tens of lines with classic SFINAE and wouldn’t be nearly as maintaineable as the expression version.

Rvalue References and Move Semantics

In C++03, temporary values (rvalues) are always immutable and semantically equivalent to their const reference type. C++11 adds a mutable rvalue type, T &&. These can be modified, allowing for move semantics.

Move constructors were added to provide the move behavior. On primitive types this does not matter, however on complicated types, moving, i.e. transferring ownership, can be very useful.

For example for std::vector, moving will merely transfer the internal buffer into the new container, deinitializing the vector that’s being moved from. This has benefits beyond performance; for example, it allows¬† implementation of unique-ownership smart pointers, where only one container owns a pointer at any one time (std::unique_ptr).

How does std::move actually work? It’s defined like this:

This will look incredibly confusing at first, and it is, so let’s break it down. The T &&¬† in this example is not an rvalue reference. When && is used with a generic type provided by the template, the meaning is special.

The key to understanding this is in how references are collapsed. Essentially, the rule is that & takes precedence. So if the T passed here is an lvalue reference, we get T & && and the whole expression is collapsed to just T &. When the T passed is an rvalue reference, we get T && && and the result is just T &&. When the argument passed is a plain rvalue (i.e. T), the result is only T.

So what std::move does is take either an rvalue OR an lvalue, strip references from it, and combine the stripped type with && to form an actual rvalue reference type and cast the given value to it.

An xvalue is given as a result, which is an object that can be moved from, and that’s represented as an rvalue reference. When passed to a function, it decays to lvalue, unless moved again (or forwarded). This means that when a value is moved across multiple calls, it must maintain it’s xvalue state.

Perfect forwarding is a related concept. For that, std::forward is provided, which is defined like this:

What is perfect forwarding? When working with functions taking generic T &&, perfect forwarding allows you to forward the value, preserving its lvalue or rvalue-ness. For example:

This “special” meaning allows the function to take lvalues as well as temporaries. So this will work:

Using perfect forwarding, you can pass the argument to another similar function without having the argument decay, i.e. the type will be exactly the same in the other function as it was, allowing for overloaded functions to resolve correctly.

Anyway, as you can see, there are two variants of forward. The first variant is used when the input is an lvalue and the second variant is used when the input is an rvalue. So, let’s consider this case:

It will expand to this:

and thanks to reference collapsing, decay to:

Now with an rvalue:

Const doesn’t make a difference here; an lvalue reference to const will expand identically to non-const. For a single value, perfect forwarding is not that useful as you can simply define an overload for each type. But it becomes very useful when you have multiple arguments in different combinations, or a variadic argument pack. Consider this:

Or this:

Imagine doing these without forwarding; an overload for every combination would be required in the latter case and it’d simply be impossible in the former case.

Constexpr

Using constexpr, you can have function calls at compile-time as well as proper compile-time variables.

A simple case is when you want to make a variable that stores a static array size. With constexpr, you can simply do this:

This was not possible before. You had to either use an enum or a preprocessor macro. That’s not all, you can also have constexpr functions and object constructors or methods:

constexpr in C++11 is rather limited, it basically only allows the return statement. It was extended in C++14 to allow conditional branches, loops, variables, and more. Many standard library functions are now marked constexpr, including move/forward and initializer list constructors and methods.

Keep in mind that constexpr does NOT imply const. They’re two separate qualifiers and need to be treated as such. On primitives this does not really matter though as copy will pretty much always pass them.

Onward to More C++!

There are plenty of reasons to use C++11 in your code, these are just a handful of the most important. Using lambdas, you can greatly simplify memory management in your code, as well as structure it better – there is no longer a need to create functions outside and then passing function pointers and extra data separately. If you use template metaprogramming, you will surely find expression SFINAE a helpful concept that can simplify complicated metaprogramming logic. Thanks to move semantics, you can now properly manage ownership of objects and transfer memory where applicable without copying. Finally, thanks to constexpr, you can avoid using preprocessor macros and do cleaner compile-time computations that don’t need to involve complicated template logic.

There’s a handful more features I’m going to cover in the next (and final) article, such as range based for loops, initializer lists and strongly typed enums. So stay tuned!

Daniel Kolesa

About Daniel Kolesa

Daniel Kolesa has been contributing to open source since 2007 and has worked with a handful open source projects. His work has been primarily on Enlightenment, starting in late 2008 with his main focus being the Enlightenment Foundation Libraries. In January 2014 he moved from the Czech Republic to the UK and joined the Samsung Open Source Group to focus more on the EFL and eventually became a maintainer of several core EFL components including Eolian and Elua. He is also a video game developer, maintaining his own OctaForge 3D engine. FInally, he is also a functional programming fan, having worked with languages such as Rust and OCaml, and having designed his own programming languages and compiler tech.

Image Credits: Tom Small

Development c++11 /

Leave a Reply

Your email address will not be published. Required fields are marked *

Comments Protected by WP-SpamShield Anti-Spam