7 Features of C++17 that will simplify your code

fenbf
21.8K views

Open Source Your Knowledge, Become a Contributor

Technology knowledge has to be shared and made accessible for free. Join the movement.

Create Content

constexpr if

This is a big one!

The static-if for C++!

The feature allows you to discard branches of an if statement at compile-time based on a constant expression condition.

if constexpr(cond)
     statement1; // Discarded if cond is false
else
     statement2; // Discarded if cond is true

For example:

template <typename T>
auto get_value(T t) {
    if constexpr (std::is_pointer_v<T>)
        return *t;
    else
        return t;
}
constexpr if simple example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <memory>
template <typename T>
auto get_value(T t) {
if constexpr (std::is_pointer_v<T>)
return *t;
else
return t;
}
int main()
{
auto pi = std::make_unique<int>(9);
int i = 9;
std::cout << get_value(pi.get()) << "\n";
std::cout << get_value(i) << "\n";
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Regarding more code samples? Hmm... constexpr if can be used to replace several tricks that were already done:

  • SFINAE technique to remove not matching function overrides from the overload set
  • you might want to look at places with C++14's std::enable_if - that should be easily replaced by constexpr if.
  • Tag dispatch

So, in most of the cases, we can now just write a constexpr if statement and that will yield much cleaner code. This is especially important for metaprogramming/template code that is, I think, complex by its nature.

A simple example: Fibonacci:

template<int  N>
constexpr int fibonacci() {return fibonacci<N-1>() + fibonacci<N-2>(); }
template<>
constexpr int fibonacci<1>() { return 1; }
template<>
constexpr int fibonacci<0>() { return 0; }

Now, it can be written almost in a 'normal' (no compile time version):

template<int N>
constexpr int fibonacci()
{
    if constexpr (N>=2)
        return fibonacci<N-1>() + fibonacci<N-2>();
    else
        return N;
}

In C++ Weekly episode 18 Jason Turner makes an example that shows that constexpr if won't do any short circuit logic, so the whole expression must compile:

if constexpr (std::is_integral<T>::value && 
              std::numeric_limits<T>::min() < 10)
{

}

For T that is std::string you'll get a compile error because numeric_limits are not defined for strings.

In the C++Now 2017: Bryce Lelbach “C++17 Features"/16th minute there's a nice example, where constexpr if can be used to define get<N> function - that could work for structured bindings.

struct S 
{
    int n;
    std::string s;
    float d;
};

template <std::size_t I>
auto& get(S& s)
{
    if constexpr (I == 0)
        return s.n;
    else if constexpr (I == 1)
        return s.s;
    else if constexpr (I == 2)
        return s.d;
}

Versus previously you would have needed to write:

template <> auto& get<0>(S &s) { return s.n; }
template <> auto& get<1>(S &s) { return s.s; }
template <> auto& get<2>(S &s) { return s.d; }
constexpr if get for structures
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
#include <iostream>
#include <string>
struct S
{
int n;
std::string s;
float d;
};
template <std::size_t I>
auto& get(S& s)
{
if constexpr (I == 0)
return s.n;
else if constexpr (I == 1)
return s.s;
else if constexpr (I == 2)
return s.d;
}
int main()
{
S obj { 0, "hello", 10.0f };
std::cout << get<0>(obj) << ", " << get<1>(obj) << "\n";
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

As you can see it's questionable which is the simpler code here. Although in this case, we've used only a simple struct, with some real world examples the final code would be much more complex and thus constexpr if would be cleaner.

More details:

MSVC 2017.3, GCC: 7.0, Clang: 3.9.

Open Source Your Knowledge: become a Contributor and help others learn. Create New Content