We are excited to see that if-constexpr has made it into C++ 17! In this post, we will look at some C++ 14 code and show how we can make it simpler and more concise using this new language feature.
Before diving into if-constexpr, it might be useful to have a quick recap of constexpr
. Introduced in C++ 11, constexpr
is a keyword that marks an expression or function as having a compile-time constant result.
And, of course, this will optimized away by the compiler:
OK, so you might be wondering what the purpose of constexpr
is. After all, isn’t the compiler smart enough to optimize functions like this, even when they are not marked as constexpr
?
The real value of constexpr
is as a guarantee that the function will be computable at compile-time. This prevents nasty side-effects sneaking into your code as it evolves, and it allows the compiler to do some clever things:
constexpr
allows for loops and recursion at compile-time without extreme boilerplate.constexpr
functions can be used as regular functions, although internally they have greater restrictions.constexpr
functions can easily be converted into regular functions as requirements change.constexpr
functions compile much quicker than the equivalent template-based solutions, which scale linearly with the depth of the template-recursion.As an example, take a look at these two compile-time implementations of the Fibonacci sequence:
In short, if-constexpr extends the compile-time subset of the C++ language to include if-statements. What’s more, if a branch of the if-constexpr is not hit, then it will not even be compiled.
With if-constexpr at your disposal, you don’t need to resort to elaborate meta-programming techniques like template pattern-matching and SFINAE.
Let’s look at some examples.
Many template meta-programs operate on variadic-type-lists. In C++ 14, getting the nth-type of an argument lists is often implemented using complex templating:
C++ 17 makes this much more intuitive:
Sometimes you want to support an alternative API. C++ 14 provides an easy way to check if an object can be used in a certain way:
Then, implementing custom behaviour in C++ 14 can be done like this:
The C++17 equivalent is much less verbose:
This is very convenient as code that belongs semantically together is not scattered across multiple functions. Furthermore, you can even define lambdas containing if-constexpr.
Often you need to find the best algorithm based on the properties of a type. There are many solutions. For instance, the STL uses “type tags” to pick the right algorithm for some given iterators:
However, once you have more complex rules, you might need a more powerful solution — SFINAE:
C++ 14:
With C++ 17 you can describe these rules with less boilerplate and in a clearer way:
This is very practical as working with if-statements is more intuitive than using a variety of language-features.
Refactoring meta-functions becomes as simple as ordinary code. With if-constexpr, worrying about ambiguous overloads and other unexpected complications is a thing of the past.