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
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:
- Unlike templates and preprocessor macros,
constexprallows for loops and recursion at compile-time without extreme boilerplate.
constexprfunctions can be used as regular functions, although internally they have greater restrictions.
constexprfunctions can easily be converted into regular functions as requirements change.
constexprfunctions 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:
So what is if-constexpr?
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.
Example 1 — getting the nth-arg
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:
Example 2 — API-shimming
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.
Example 3 — Compile-time algorithm-picking
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:
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.