# Fixed Size Parameter Packs

ISO/IEC JTC1 SC22 WG21 N4072 — 2014-05-26

Maurice Bos <m-ou.se@m-ou.se>

## Summary

This document proposes an extension to the `T...`-notation for parameter packs: `T...[N]` to give the pack a fixed size. (This might sound close to useless at first, but this document should hopefully convince you otherwise.) This allows for elegant and readable solutions for problems that used to require a lot of confusing boiler plate code, such as taking a variable number of parameters of the same type.

## The Proposed Idea

In both template parameter lists and function parameter lists, `T...[N] p` would make `p` a parameter pack of exactly `N` `T`s. `N` must be a constant expression.

So, `void foo(int...[3] args)` would make `args` a pack of three integers, such that `foo` can be called as `foo(1,2,3)`. Similarly, `template<typename...[2] Args> void foo(Args... args)` would make `Args` a pack of two typenames (and therefore `args` a pack of two objects of those two types).

## Use Cases and Examples

This section describes a few use cases. All first describe the scenario, then give a solution with what's currently possible with C++14, and then give a solution using the proposed idea.

The first two/three are the most important and interesting use cases.

### 1) A constant (dependent) number of parameters

#### Scenario

Imagine we're making a class `my_vector3`, which represents a three-dimensional mathematical vector of integers. It has a constructor `my_vector3(int, int, int)` that takes exactly three integers, as you'd probably expect of such a class:

    struct my_vector3 {
        // ...
        my_vector3(int x, int y, int z)
            : values{x, y, z} {}
    };

Now we want to turn this into a class template `my_vector<N>`, such that the number of dimensions is not fixed to three. Logically, it should have a constructor that takes exactly `N` integers, but how would we do that?

#### Current solution

This is probably the closest we can get with C++14:

    // Helper template gen_tuple:
    // gen_tuple<3, int>::type becomes std::tuple<int, int, int>, etc.

    template<unsigned int N, typename T> struct gen_tuple {
        using type = decltype(
            std::tuple_cat(
                std::declval<std::tuple<T>>(),
                std::declval<typename gen_tuple<N-1, T>::type>()
            )
        );
    };

    template<typename T> struct gen_tuple<0, T> {
        using type = std::tuple<>;
    };

    namespace impl {
        // This template will only ever be instantiated with an std::tuple of ints.
        template<typename> struct my_vector;
        template<typename... ints> struct my_vector<std::tuple<ints...>> {
            static constexpr unsigned int N = sizeof...(ints);
            // ...
            my_vector(ints... v) : values{v...} {}
        };
    }

    template<unsigned int N>
    using my_vector = impl::my_vector<typename gen_tuple<N, int>::type>;

Not only does this take a lot of code, defining `impl::my_vector` this way is confusing and error prone. (Also, note that using this solution, `N` can not be inferred when calling something like `template<unsigned int N> void foo(my_vector<N>)`. However, this can be fixed by changing the alias template for a class template that has `impl::my_vector` as a base and inherits the constructors.)

#### New solution

With the 'fixed size parameter packs' feature this document proposes, the `my_vector` template can simply be written as:

    template<unsigned int N>
    struct my_vector {
        // ...
        my_vector(int...[N] v) : values{v...} {}
    };

Beautiful!

### 2) A variable number of parameters of a specific type

#### Scenario

Let's say we have a function `void foo3(int, int, int)`, that does something with three integers. However, instead of also writing a `foo4` to do it with four integers, we'd like to turn it into a template such that we have `foo<N>` for any `N`.

#### Current solution

    template<bool...> constexpr bool all = true;
    template<bool head, bool... tail> constexpr bool all<head, tail...> = head && all<tail...>;

    template<
        typename... T,
        typename = std::enable_if_t<
            all<std::is_convertible<T, int>::value...>
        >
    >
    void foo(T&&...);

A few notes:

- `foo3(1, 2u, 3l)` would convert the `2u` and `3l` to `int`s before the function call, `foo(1, 2u, 3l)` would convert them later, inside the function. (Which doesn't really matter for integral types, but for other types it could be more significant.)
- Because of this, each of the following instantiate the function template again with different `T...`: `foo(1, 2, 3)`, `foo(1u, 2l, 3)`, etc.
- Errors can be confusing when calling `foo` with wrong parameters.

So, this requires some boiler plate code, it is not very readable, and although it comes close, it is not exactly what we wanted.

#### New solution

    template<unsigned int N> void foo(int...[N] v);

Beautiful!

#### Discussion

See Discussion of Use Case 2 for a small discussion about this use case.

### 3) A variable number of parameters of the same type

#### Scenario

Suppose we have a class template `iterleaving_iterator<ForwardIterator, N>` which is an iterator that interleaves `N` streams from iterators of the type `ForwardIterator`. (Such that a `interleaving_iterator<vector<int>::iterator, 3>` allows us to iterate over `{1,2,3}`, `{10,20,30}`, and `{100,200,300}` at once as `{1,10,100,2,20,200,3,30,300}`.)

Now, we want a function `interleave` that takes itererators and returns such an `interleaving_iterator`, with both template parameters automatically deduced. (Such that `auto it = interleave(a.begin(), b.begin(), c.begin());` works.)

#### Current solution

    template<typename... T> struct all_the_same {};
    template<typename T> struct all_the_same<T> {
        using type = T;
    };
    template<typename Head, typename... Tail> struct all_the_same<Head, Head, Tail...>
        : all_the_same<Head, Tail...> {};

    template<typename... T>
    interleaving_iterator<typename all_the_same<T>::type, sizeof...(T)> interleave(T... it) {
        return { it... };
    }

#### New solution

    template<typename Iterator, unsigned int N>
    interleaving_iterator<Iterator, N> interleave(Iterator...[N] it) {
        return { it... };
    }

Other than that this code is much easier to write and read, the error message a user gets when accidentally calling `interleave` with arguments of different types can just explain the user that `interleave` expects all `it` arguments to be of the same type.

### 4) A fixed number of parameters (of any mixed types)

#### Scenario

With `template<unsigned int N> struct foo`, add a member function `foo<N>::bar` taking exactly N parameters of any type.

#### Current solution

    template<unsigned int N>
    struct foo {
        template<typename... T, typename = std::enable_if_t<sizeof...(T) == N>> void bar(T... v);
    };

#### New solution

    template<unsigned int N>
    struct foo {
        template<typename...[N] T> void bar(T... v);
    };

### 5) Just to avoid repetition

And as a last example, the proposed feature could be used just to avoid repeating parameters:

#### Current solution

    void foo(int x, int y, int z, int w) { do_magic(x, y, z, w); }

    template<typename A, typename B, typename C, typename D, typename E>
    void bar(A a, B b, C c, D d, E e);

(Of course, `typename...` and `enable_if_t` with `sizeof...` could be used for `bar`, but that was already used in use case 4.)

#### New solution

    void foo(int...[4] v) { do_magic(v...); }

    template<typename...[5] T>
    void bar(T... v);

## Discussion of Use Case 2

In C++, it's very easy to make a function that takes

1. two integers: `void foo(int, int);`
2. two parameters of any type: `template<typename A, typename B> void foo(A, B);`
3. any number of integers.
4. any number of parameters of any type: `template<typename... T> void foo(T...);`

Unfortunately, option 3 would be useful but is missing. The only option now is to use option 4 combined with `enable_if` and `is_convertible`. (Imagine a world where option 1 doesn't exist, such that the only way is to use option 2 combined with `enable_if` and `is_convertible`.)

A suggestion I've often heard as a solution to this problem is this:

    void foo(int... a);

(This could seem very logical, since the `...` here is used the same as in `template<typename...>`.)

Apart from that the syntax itself is a problem (since `void foo(int...)` is already a C `vararg` function), there's another problem: This defines a template, not just a function, without using the keyword `template`. From recent discussions about whether to allow `void foo(auto a)` or not to define a function template, this seems like a bad idea. So, we want to add the `template` keyword explicitly:

    template</* What do we put here? */> void foo(int... a);

The only difference between the instantiations of `foo` is the number of parameters, so it'd make sense to make this the template parameter:

    template<unsigned int> void foo(int... a);

But we need to specify that this integer is the length of the pack. To do that, we would end up with something like:

    template<unsigned int N> void foo(int...[N] v);

And this is exactly what this paper proposes.

## Template Argument Deduction

With `template<typename T, unsigned int N> void foo(T...[N]) {}`, `T` can be deduced for any `N > 0` using the same rules as used for `template<typename T> void bar(T, T, T) {}`.

More interesingly, it is important that `N` can be deduced: `N` would be deducable if the parameter list ends with a fixed size parameter pack of size `N`. For example, in these cases template argument deduction would work:

    template<unsigned int N> void f(int...[N]) {}
    f(1,2,3); // N is deduced as 3.

    template<unsigned int N> void f(int, int...[N]) {}
    f(1,2,3); // N is deduced as 2.

    template<unsigned int N, unsigned int M> void f(int...[N], int...[M]) {}
    f<2>(1,2,3); // M is deduced as 1. (N can't be deduced, but is given.)

And it would work for none of these cases:

    template<unsigned int N> void f(int...[N], int) {}
    f(1,2,3);

    template<unsigned int N> void f(int...[N], int...[N]) {}
    f(1,2,1,2);

    template<unsigned int N> void f(int...[N*2]) {}
    f(1,2,3,4);

## Technical Specification

TODO

(I could use some help here.)

## Acknowledgements

I want to thank the people on Freenode's ##C++ and cpp-proposals@isocpp.org I discussed this idea with. Also, thank you, Filip Roséen <filip.roseen@gmail.com>, for your helpful feedback and coming up with the C++14 solution for use case 1.

N4072 — Fixed Size Parameter Packs — Maurice Bos <m-ou.se@m-ou.se>