Search code examples
c++templatessyntactic-sugar

Can I make a function to accept N ints where N is a template parameter?


I have a situation where I need to pass in a variable number of integers (known at compile time).

I need to achieve something like this:

template <unsigned N>
void myFunc(std::array<int, N> values)
{
    // Do something useful with the values
}

I hoped the above would work and could be called like this, conveniently without creating a temporary object;

myFunc<4>({1, 2, 3, 4});

This did in fact work, but the problem is that this also works...

myFunc<4>({1, 2});

which of course leaves two of the values uninitialised, unbeknownst to me and the user.

I believe this would work:

template <unsigned N>
void myFunc(std::vector<int> values)
{
    assert(values.size() == N);
    // Do something useful with the values
}
myFunc<4>({1, 2}); // Goes bang
myFunc<4>({1, 2, 3, 4}); // OK

Unfortunately I can't allocate memory in my environment), so I have to do everything on the stack (and can't just accept a vector of which I could check the size).

I see myself as having two options.

  1. Use an object which is compile-time sized and based off of the length of the initialiser list
  2. Use some magic recursive C++ parameter pack syntax to allow a variable number of ints, but that's beyond me.

Solution

  • std::array<int, 4> arr{ 0, 1}; is fine; the elements that aren't specified are simply zero-initialized. If you don't want this to work, std::array isn't the right class to use for you (or to be more precise: you cannot have it impose the desired restrictions).

    You could of course create a variadic template and restrict the parameters according to your requirements:

    template<size_t N, std::convertible_to<int>...Args>
    requires (sizeof...(Args) == N)
    void f(Args&&...args)
    {
        std::array<int, N> values{args...};
        for (auto e : values)
        {
            std::cout << e << '\n';
        }
    }
    
    ...
    
    f<4>(1, 2, 3, 4);
    

    Why specify the number of elements multiple times though? Just use C++17's functionality doing CTAD and deduce the template parameter of the function based on the argument type:

    template<size_t N>
    void f2(std::array<int, N> const& values)
    {
        for (auto e : values)
        {
            std::cout << e << '\n';
        }
    }
    
    ...
    
    f2(std::array{ 1, 2, 3, 4 });
    

    or avoid the necessity of using a template by simply using an std::initializer_list<int> as function parameter:

    void f3(std::initializer_list<int> values)
    {
        for (auto e : values)
        {
            std::cout << e << '\n';
        }
    }
    
    ...
    
    f3({ 1, 2, 3, 4 });