Tricks with Default Template Arguments

Just like regular function parameters, template parameters can also have default parameters. For class templates, this behaves mostly just like default function arguments: if you pass fewer template arguments than required, default template arguments are used to fill the remaining places. However, for function templates, it gets more complicated as template parameters for functions can be deduced by the normal function arguments. This leads to some interesting side-effects. In particular, default arguments of template parameters don’t need to be put at the end!

Let’s take a look at a couple of things we can do with default template arguments.

» read more »
Jonathan

constexpr is a Platform

Let me share a useful insight with you: constexpr is a platform.

Just like you write code that targets Windows or a microcontroller, you write code that targets compile-time execution. In both cases you restrict yourself to the subset of C++ that works on your target platform, use conditional compilation if your code needs to be portable, and execute it on the desired target platform. You can thus view constexpr as another platform you can target; it just so happens to be run by your compiler.

This insight can answer a lot of design questions surrounding constexpr.

» read more »
Jonathan

Technique: Immediately-Invoked Function Expression for Metaprogramming

Common C++ guidelines are to initialize variables on use and to make variables const whenever possible. But sometimes a variable is unchanged once initialized and the initialization is complex, like involving a loop. Then an IIFE – immediately-invoked function expression – can be used: the variable is initialized by a lambda that computes the value, which is then immediately invoked to produce the value. Then the variable is initialized on use and can also be made const.

I’ve been recently working on a meta-programming library where I found IIFEs useful in a slightly different context – computing type information.

TL;DR: decltype([] { ... } ())!

» read more »
Jonathan

Implementation Challenge: Replacing std::move and std::forward

When C++11 introduced move semantics, it also added two important helper functions: std::move and std::forward. They are essential when you want to manually indicate that you no longer care about an object or need to propagate the value category in generic code. As such, I’ve used them countless times in the past.

However, they are functions. Plain, old, standard library functions.

This is problematic for multiple reasons.

» read more »
Jonathan

Nifty Fold Expression Tricks

Suppose you need to have a variadic function and want to add all arguments together. Before C++17, you need two pseudo-recursive functions:

template <typename H, typename ... T>
auto add(H head, T... tail)
{
    return head + add(tail...);
}

template <typename H>
auto add(H head)
{
    return head;
}

However, C++17 added fold expressions, making it a one-liner:

template <typename H, typename ... T>
auto add(H head, T... tail)
{
    return (head + ... + tail);
    // expands to: head + tail[0] + tail[1] + ...
}

If we’re willing to abuse operator evaluation rules and fold expressions, we can do a lot more. This blog posts collects useful tricks.

» read more »
Jonathan