Simplify code with 'if constexpr' in C++17 
Tags: cpp, cpp17, programming
Published: 2018-04-23 08:39:05

---
title: Simplify code with 'if constexpr' in C++17 
published: true
description: With C++17 we have an amazing way of writing compile time if statements. See how we can simplify code.
tags: cpp, cpp17, programming
---


[![Simplify your code with if constexpr in C++17](https://2.bp.blogspot.com/-eMmMqux9UJs/WqWJfNeWkAI/AAAAAAAADTs/gmG0VPQFcWESMJTGa-K-ORRvTUM_rkM2wCLcBGAs/s1600/ifconstexpr.png)](http://www.bfilipek.com/2018/03/ifconstexpr.html)

Before C++17 we had a few, quite ugly looking, ways to write `static if` (`if` that works at compile time) in C++: you could use tag dispatching or  [SFINAE](http://www.bfilipek.com/2016/02/notes-on-c-sfinae.html) (for example via `std::enable_if`). Fortunately, that's changed, and we can now take benefit of `if constexpr`! 

Let's see how we can use it and replace some `std::enable_if` code.


**One note:** this article comes from my blog: [Bartek's coding blog: Simplify code with 'if constexpr' in C++17](https://www.bfilipek.com/2018/03/ifconstexpr.html).

## Intro

Static if in the form of `if constexpr` is an amazing feature that went into C++17. Recently @Meeting C++ there was a post where Jens showed how he simplified one code sample using `if constexpr`: [How if constexpr simplifies your code in C++17](http://meetingcpp.com/blog/items/How-if-constexpr-simplifies-your-code-in-Cpp17.html).

I've found two additional examples that can illustrate how this new feature works:

* Number comparisons
* Factories with a variable number of arguments

I think those examples might help you to understand the `static if` from C++17.

But for a start, I'd like to recall the basic knowledge about `enable_if` to set some background.

## Why compile time if?

At first, you may ask, why do we need `static if` and those complex templated expressions... wouldn't normal `if` just work?

Here's a test code:

```cpp
template <typename T>
std::string str(T t)
{
    if (std::is_same_v<T, std::string>) // or is_convertible...
        return t;
    else
        return std::to_string(t);
}
```

The above routine might be some simple utility that is used to print stuff. As `to_string` doesn't accept `std::string` we can test and just return `t` if it's already a string. Sound simple... but try to compile this code:

```cpp
// code that calls our function
auto t = str("10"s);
```

You might get something like this:

```
In instantiation of 'std::__cxx11::string str(T) [with T = 
std::__cxx11::basic_string<char>; std::__cxx11::string =
 std::__cxx11::basic_string<char>]':
required from here
error: no matching function for call to 
'to_string(std::__cxx11::basic_string<char>&)'
    return std::to_string(t);
```

`is_same` yields `true` for the type we used (string) and we can just return `t`, without any conversion... so what's wrong?

Here's the main point:

The compiler compiled both branches and found an error in the `else` case. It couldn't reject "invalid" code for this particular template instantiation.

So that's why we need static if, that would "discard" code and compile only the matching statement.


## `std::enable_if`

One way to write static if in C++11/14 is to use `enable_if`.

 `enable_if` (and `enable_if_v` since C++14). It has quite strange syntax:

```cpp
template< bool B, class T = void >  
struct enable_if;
```

`enable_if` will evaluate to `T` if the input condition `B` is true. Otherwise, it's [SFINAE](http://www.bfilipek.com/2016/02/notes-on-c-sfinae.html), and a particular function overload is removed from the overload set.

We can rewrite our basic example to:

```cpp
template <typename T>
std::enable_if_t<std::is_same_v<T, std::string>, std::string> strOld(T t)
{
    return t;
}

template <typename T>
std::enable_if_t<!std::is_same_v<T, std::string>, std::string> strOld(T t)
{
    return std::to_string(t);
}
```

Not easy... right?

See below how we can simplify such code with `if constexpr` from C++17. After you read the post, you'll be able to rewrite our `str` utility quickly.

## Use Case 1 - Comparing Numbers

At first, let's start with a simple example: `close_enough` function, that works on two numbers. If the numbers are not floating points (like when we have two `ints`), then we can just compare it. Otherwise, for floating points, it's better to use some epsilon.

I've found this sample from at [Practical Modern C++ Teaser](https://h-deb.clg.qc.ca/WG21/PracticalCpp/Temperature.html) - a fantastic walkthrough of modern C++ features by [Patrice Roy](https://twitter.com/PatriceRoy1). He was also very kind and allowed me to include this example.

C++11/14 version:

```cpp
template <class T>
constexpr T absolute(T arg) {
   return arg < 0 ? -arg : arg;
}

template <class T>
constexpr enable_if_t<is_floating_point<T>::value, bool> 
close_enough(T a, T b) {
   return absolute(a - b) < static_cast<T>(0.000001);
}
template <class T>
constexpr enable_if_t<!is_floating_point<T>::value, bool> 
close_enough(T a, T b) {
   return a == b;
}
```

As you see, there's a use of `enable_if`. It's very similar to our `str` function. The code tests if the type of input numbers is `is_floating_point`. Then, the compiler can remove one function from the overload resolution set.

And now, let's look at the  C++17 version:

```cpp
template <class T>
constexpr T absolute(T arg) {
   return arg < 0 ? -arg : arg;
}

template <class T>
constexpr auto precision_threshold = T(0.000001);

template <class T>
constexpr bool close_enough(T a, T b) {
   if constexpr (is_floating_point_v<T>) // << !!
      return absolute(a - b) < precision_threshold<T>;
   else
      return a == b;
}
```

Wow... so just one function, that looks almost like a normal function. With nearly "normal" if :)

`if constexpr` evaluates `constexpr` expression at compile time and then discards the code in one of the branches.

BTW: Can you see some other C++17 features that were used here?

### Play with the code

Go to the main article with a live code example:
[Bartek's coding blog: Simplify code with 'if constexpr' in C++17](https://www.bfilipek.com/2018/03/ifconstexpr.html#play-with-the-code)


## Use case 2 - factory with variable arguments

In the item 18 of Effective Modern C++ Scott Meyers described a method called `makeInvestment`:

```cpp
template<typename... Ts> 
std::unique_ptr<Investment> 
makeInvestment(Ts&&... params);
```

There's a factory method that creates derived classes of `Investment` and the main advantage is that it supports a variable number of arguments!

For example, here are the proposed types:

```cpp
class Investment
{
public:
    virtual ~Investment() { }
    
    virtual void calcRisk() = 0;
};

class Stock : public Investment
{
public:
    explicit Stock(const std::string&) { }
    
    void calcRisk() override { }
};

class Bond : public Investment
{
public:
    explicit Bond(const std::string&, const std::string&, int) { }
    
    void calcRisk() override { }
};

class RealEstate : public Investment
{
public:
    explicit RealEstate(const std::string&, double, int) { }
    
    void calcRisk() override { }
};
```

The code from the book was too idealistic, and didn't work - it worked until all your classes have the same number and types of input parameters:

[Scott Meyers: Modification History and Errata List for Effective Modern C++](http://www.aristeia.com/BookErrata/emc++-errata.html):

> The `makeInvestment` interface is unrealistic, because it implies that all derived object types can be created from the same types of arguments. This is especially apparent in the sample implementation code, where are arguments are perfect-forwarded to all derived class constructors.

For example if you had a constructor that needed two arguments and one constructor with three arguments, then the code might not compile:

```cpp
// pseudo code:
Bond(int, int, int) { }
Stock(double, double) { }
make(args...)
{
  if (bond)
     new Bond(args...);
  else if (stock)
     new Stock(args...)
}
```

Now, if you write `make(bond, 1, 2, 3)` - then the `else` statement won't compile - as there no `Stock(1, 2, 3)` available! To work, we need something like static if - if that will work at compile time, and will reject parts of the code that don't match a condition.

Some posts ago, with the help of one reader, we came up with a working solution (you can read more in [Bartek's coding blog: Nice C++ Factory Implementation 2](http://www.bfilipek.com/2016/03/nice-c-factory-implementation-2.html)).

Here's the code that could work:

```cpp
template <typename... Ts> 
unique_ptr<Investment> 
makeInvestment(const string &name, Ts&&... params)
{
    unique_ptr<Investment> pInv;
    
    if (name == "Stock")
        pInv = constructArgs<Stock, Ts...>(forward<Ts>(params)...);
    else if (name == "Bond")
        pInv = constructArgs<Bond, Ts...>(forward<Ts>(params)...);
    else if (name == "RealEstate")
        pInv = constructArgs<RealEstate, Ts...>(forward<Ts>(params)...);
            
    // call additional methods to init pInv...
    
    return pInv;
}
```

As you can see the "magic" happens inside `constructArgs` function.

The main idea is to return `unique_ptr<Type>` when Type is constructible from a given set of attributes and `nullptr` when it's not.

### Before C++17

In my previous solution (pre C++17) we used `std::enable_if` and it looked like that:

```cpp
// before C++17
template <typename Concrete, typename... Ts>
enable_if_t<is_constructible<Concrete, Ts...>::value, unique_ptr<Concrete>>
constructArgsOld(Ts&&... params)
{
    return std::make_unique<Concrete>(forward<Ts>(params)...);
}

template <typename Concrete, typename... Ts>
enable_if_t<!is_constructible<Concrete, Ts...>::value, unique_ptr<Concrete> >
constructArgsOld(...)
{
    return nullptr;
}
```

[std::is_constructible @cppreference.com](http://en.cppreference.com/w/cpp/types/is_constructible) - allows us to quickly test if a list of arguments could be used to create a given type.

In C++17 there's a helper:

```cpp
is_constructible_v = is_constructible<T, Args...>::value;
```

So we could make the code shorter a bit...

Still, using `enable_if` looks ugly and complicated. How about C++17 version?

### With `if constexpr`

Here's the updated version:

```cpp
template <typename Concrete, typename... Ts>
unique_ptr<Concrete> constructArgs(Ts&&... params)
{ 
   if constexpr (is_constructible_v<Concrete, Ts...>)
      return make_unique<Concrete>(forward<Ts>(params)...);
   else
       return nullptr;
}
```

We can even extend it with a little logging features, using fold expression:

```cpp
template <typename Concrete, typename... Ts>
std::unique_ptr<Concrete> constructArgs(Ts&&... params)
{ 
    cout << __func__ << ": ";
    // fold expression:
    ((cout << params << ", "), ...);
    cout << "\n";
    
    if constexpr (std::is_constructible_v<Concrete, Ts...>)
        return make_unique<Concrete>(forward<Ts>(params)...);
    else
       return nullptr;
}
```

Cool... right? :)

All the complicated syntax of `enable_if` went away; we don't even need a function overload for the     `else` case. We can now wrap expressive code in just one function.

`if constexpr` evaluates the condition and only one block will be compiled. In our case, if a type is constructible from a given set of attributes, then we'll compile `make_unique` call. If not, then `nullptr` is returned (and `make_unique` is not even compiled).

### Play with the code

Go to the main article with a live code example:
[Bartek's coding blog: Simplify code with 'if constexpr' in C++17](https://www.bfilipek.com/2018/03/ifconstexpr.html#play-with-the-code-1)


## Wrap up

Compile-time `if` is an amazing feature that greatly simplifies  templated code. What's more, it's much expressive and nicer than previous solutions: tag dispatching or `enable_if` (SFINAE). Now, you can easily express yours intends similarly to "run-time" code.

In this article, we've touched only basic expressions, and as always I encourage you to play more with this new feature and explore.

## More from the Author

Bartek recently published a book - [**"C++17 In Detail"**](https://leanpub.com/cpp17indetail?utm_source=devto&utm_campaign=simpleifconst)- rather than reading the papers and C++ specification drafts, you can use this book to learn the new