Embrace C++14 in Your Project

I recently wrote a series on the benefits of the C++11 spec. However, C++14 is a newer specification that has been out for a while and adds a few more exciting language features.

Clang 3.8 was recently released, and this marks a stable implementation of C++14 in my opinion. In the past I’ve encountered some nasty bugs while implementing new features, preventing me from actually using them. This is no longer the case in version 3.8 and it therefore seem to be fairly safe.

In terms of other C++ toolchains, the 5.x series of GCC cover C++14 pretty well, and MS Visual Studio offers partial support in their own compiler with full support coming soon through the officially supported Clang compiler.

Let’s take a look at some of the new features that make this version great.

Variable Templates

In my opinion, variable templates are the single most important C++14 feature. They allow you to turn variables into templates and specialize them:

template
constexpr T stuff = T(5);

template<>
constexpr const char *stuff = "hello world";

Now let’s take a look at a practical case. Say that we need to implement a compile-time trait that will check if two types are the same. In C++11, the solution would be something like this:

template<typename, typename>
struct IsSame: std::integral_constant<bool, false> {};

template
struct IsSame<T, T>: std::integral_constant<bool, true> {};

Then its usage would look like this:

constexpr bool the_same = IsSame<T1, T2>::value;

With variable templates, we can reduce it to simpler code:

template<typename, typename> constexpr bool IsSame = false;
template constexpr bool IsSame<T, T> = true;

And usage is simpler as well.

constexpr bool the_same = IsSame<T1, T2>;

The most important thing for the user is that it’s possible to drop the ::value at the end, making the code clearer and more concise.

There are other benefits though: particularly composability. Let’s say we need to check multiple things and turn that into a single trait. In C++11, it would look like this:

template
struct IsObject: std::integral_constant<bool, !IsFunction::value &&
                                              !IsVoid::value &&
                                              !IsReference::value> {};

With variable templates, you can reduce it to something as simple as this:

template
constexpr bool IsObject = !IsFunction && !IsVoid && !IsReference;

Obviously, usage remains simple.

Unfortunately, standard type traits in the C++ stdlib were not updated for C++14. There will be _v-suffixed versions of traits implemented in C++17, but that’s still a long way ahead.

Enhanced Lambdas

In C++14, lambdas can have generic parameters. Therefore, this will work:

auto add_two = [](auto a, auto b) { return a + b; };

do_something(add_two(5, 10)); /* will treat auto as int */
do_something(add_two(obj_1, obj_2)); /* for objects that overload operator+ */

Keep in mind that auto parameters are not allowed in regular, non-lambda functions.

C++14 also allows you to capture generic expressions! This works really nicely with move semantics:

SomeMovableObject obj; /* allocate storage here */
thread_pool.push([o = std::move(obj)]() {
    /* obj's resources have been moved into the lambda, no longer owned outside */
});

Previously, doing something like this was a lot more tedious.

Other Important Features

Constexpr has been improved, and It’s now possible to use conditionals, switches, and loops inside of constexpr functions. Variables without initializers are also allowed in addition to static and thread_local variables.

Functions can now fully deduce their return type without trailing arrow syntax with decltype. This can happen in most cases with a small exception: when a function is recursive, its first return statement must not be a recursive call.

Speaking of type inference, an alternative to auto was added. It can be used like this:

decltype(auto) x = y;

While normal auto will always decay the type it’s inferring (always resulting in non-reference type) and auto & or auto && will always infer their respective reference types, decltype(auto) follows decltype semantics. Therefore, it infers either reference or non-reference types as fit.

Deprecated function attributes are now supported by default. Their usage looks like this:

[[deprecated]] void some_func();

[[deprecated("this function is deprecated, use bar()")]] void foo();

This attribute will cause the compiler to emit warnings. Various compilers had support for this already, but now it’s standard with a defined syntax, so you can rely on it.

Finally, there are a handful of minor changes that are notable including the new binary integer literals with a pretty standard syntax 0b1010. Integer and floating point literals can also now contain separators like 1'000'000. Lastly, it’s now possible to initialize structures that have default member initializers using aggregate initializer lists.

Conclusion

C++14 is a welcome expansion of the C++11 standard. It adds a handful of very useful features that improve code readability and reduce size and verbosity. Some of these features, particularly variable templates, will be very useful once C++17 is finalized since having a “nice” syntax will be crucial for usage with the C++17 Concepts feature.

Keep in mind, while Clang versions before 3.8 advertise C++14 support, the variable templates implementation in the earlier versions has a major bug that makes it impossible to use them in template parameters. Therefore, if you want to rely on it, use Clang 3.8 or newer or GCC 5. It’s a great time to start using C++14 an I highly encourage you to do so.

Author: Daniel Kolesa

Daniel has been contributing to open source since 2007 and has been involved with the EFL and the OctaForge 3D game engine in addition to other projects.