How if constexpr simplifies your code in C++17

published at 22.02.2018 16:24
Save to Instapaper Pocket

So, yesterday we had a little live coding session at my C++ User Group Düsseldorf. I want to recreate some of this code, to show how C++17 actually does help quite a bit with making code shorter and more expressive. Since I don't have a local C++17 compiler installed, I use godbolt and wandbox to test some of the examples.

So, the original code was two member functions, one template function f(T t) for capturing all input, and one overload for std::string f(std::string s). The code was a simple logger/tracer, so that the string overload did not log the whole string, only the first n characters + "...":

template<class T>
void log(T t)
{
    logstream << t;
}

void log(std::string s)
{
    ...
}

So, obviously we had a short thought on making s a const reference, but then thought, wait, actually its much better to do this with a string_view! So, the second function changes to std::string_view. Here is the first surprise: now string arguments get handled by the template function, not the string_view overload. But thats easily fixable with enable_if:

template< class T, typename = std::enable_if_t<  !std::is_convertible_v< T,std::string_view>> >
void log(const T& t)
{
    std::cout << "t" << t;
}

So this function now is only visible, if T can't be converted string_view. This is an elegant solution for C++14, but in C++17, one can use if constexpr to handle both cases in the same function. You'll just have to break some old habits like overloading functions in the first place and then using enable_if to get some corner cases right...

So with if constexpr this whole code collapses to this one function:

template<class T>
void log(const T& t)
{
    if constexpr(std::is_convertible_v<T,std::string_view>)
        //handle anything convertible to string_view
    else
        // log fully
}

I guess there could be a better way to find string like types to shorten text for logging then going for is_convertible with string_view, but that is where this short journey of pair programming in C++17 lead us. There is now only one function, where previous were two, the code is all at the place where it makes sense.

But there is a difference, previously the string_view overload was called, and a string_view constructed. This is now not the case, so this has to be dealt with either restricting the selection of if constexpr to a subset of types with the needed interface in the generic code, or converting manually to string_view.

If you want to learn more about C++17 or constexpr, Jason Turner has some practical advice for you:

Practical C++17

Practical constexpr

Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!