A Tour of Morfa

Variadic templates

A Variadic template is declared by putting ellipsis ... after the last template parameter:

template <T, V...> const Length = 1 + V.length;

When the template Length is instantiated, the parameter V is bound to a sequence, or tuple, of template arguments. The expression V.length evaluates to the number of elements in this tuple. So for example:

static assert (Length<int> == 1);             // V is bound to the empty tuple
static assert (Length<int, text, bool> == 3); // V is bound to (text, bool)
static assert (Length<int, float, 42> == 3);  // V is bound to (42, float)

As seen in the last example, a tuple may contain any valid template arguments, not only types.

Variadic templates are often used in conjunction with function types; usually, the tuple parameter is used as a list of types for a function type. For example, you may introduce an alias for a bool-valued function with any argument types as follows:

template <Args...> alias Test = func(Args): bool;

Now, Test<int,float> is an alias for the function type func(int,float): bool.

A template parameter may also be deduced. For example, the following const template that checks if the argument is a function type, uses a deduced template parameter to capture the list of argument types for a function type (note that the ellipsis is attached to the parameter declaration, not the parameter's occurrence in the specification for T):

template <T is func(Args): Result; deduce Result, Args...> const IsFuncType = true;
template <T> const IsFuncType = false;

static assert (IsFuncType<func(): bool>);              // Args = ()
static assert (IsFuncType<func(int): void>);           // Args = (int)
static assert (IsFuncType<func(bool, float): text[]>); // Args = (bool, float)

static assert (not IsFuncType<int>);
static assert (not IsFuncType<(func(float): int)[]>);

Accessing elements of an argument tuple

Individual elements of a tuple may be accessed using the indexing brackets. Consider the following template:

template <I: int>
struct Elem
{
    static template <Types...>
    func Of(args: Types): Types[I]
    { 
        return args[I]; 
    }
}

Types[I] is the I-th element of the argument tuple Types. More interestingly, the parameter args captures the whole tuple of arguments to the function Of, and args[I] is the I-th function argument:

unittest
{
    assert (Elem<0>.Of(3.14, false)   == 3.14);
    assert (Elem<1>.Of(1, "two", 0.3) == "two");
}

Variadic templates and template refinement

If two variants of a template—one variadic and the other one not—match a given tuple of arguments then the non-variadic one is selected as more specific:

template <T,U>    const TwoTypes = true;
template <V...>   const TwoTypes = false;

static assert (not TwoTypes<int>);
static assert (    TwoTypes<int, bool>);
static assert (not TwoTypes<5, bool>);
static assert (not TwoTypes<int, bool, text>);