Integer sequence as a non-type template parameter

Viewed 114

I'd like to pass several arbitrary length integer sequences as non-type parameters to a template class, so the instantiation would look something like this:

Foo<Bar, {1,2,3,4}, {5,6,7}> foo;

Is there a simple way to do this, possibly with C++11?

*** EDIT Ok, here's a more specific situation:

#include <vector>
using std::vector;

void f(vector<int> const & vec1, vector<int> const & vec2) {
    // do something with vec1 and vec2
}

template<MagicalSequenceType & seq1, MagicalSequenceType & seq2>
struct Foo {
    Foo(){
        f(vector<int>(seq1), vector<int>(seq2));
    }
};

main(){
    // I want to be able to do something like this:
    // ideally with syntax this elegant but not necessary
    Foo < {1,2,3,4}, {5,6,7} > foo;
}

This is a bit on a pseudocode level. I guess MagicalSequenceType is what I'm after, be it an actual type or some syntactic template trickery :)

3 Answers

The standard doesn't allow braced init list {1,2,3,4} to be a template argument. This can be seen from temp.names#1:

template-argument:
    constant-expression
    type-id
    id-expression 

And since {1,2,3,4} is not any of the above three listed constructs, it cannot be used as a template argument.

Additionally note that {1,2,3,4} is not an expression and does not have a type.

Here is an example on how to use arbitrary length integer sequences as template parameter in c++11. It requires a bit of template magic as we can see :P

#include <array>
#include <utility>
#include <iostream>

// Crude c++14 integer_sequence
template<int...>
struct ints { };

// Turn an integer sequence into a std::array
template<int... Is>
constexpr std::array<int, sizeof...(Is)>
ints_to_array(ints<Is...>)
{
    return std::array<int, sizeof...(Is)>{Is...};
}

// Get the n'th sequence from a template parameter pack
template<std::size_t Idx, class First, class... Sequences>
struct get_n
{
    static_assert(Idx < sizeof...(Sequences) + 1, "Idx must be in range"); 
    using type = typename get_n<Idx - 1, Sequences...>::type;
};

template<class First, class... Sequence>
struct get_n<0, First, Sequence...>
{
    using type = First;
};

struct Bar { };

// The class needing multiple integer sequences
template<class T, class... Sequences>
struct Foo
{
    void function_using_second_sequence()
    {
        using sequence = typename get_n<1, Sequences...>::type;
        constexpr auto vals = ints_to_array(sequence());
        for (auto i : vals) {
            std::cout << i << " ";
        }
        std::cout << '\n';
    }
};

int main()
{
    Foo<Bar, ints<1, 2, 3, 4>, ints<5, 6, 7>> foo;
    foo.function_using_second_sequence();
}

Try it on godbolt

EDIT: In c++20 it is much simpler :P

#include <array>
#include <iostream>
#include <utility>

template<std::size_t Idx, class First, class... Args>
constexpr decltype(auto)
get_n(First&& first, Args&& ...args)
{
    if constexpr (Idx == 0)
        return std::forward<First>(first);
    else
        return get_n<Idx - 1>(std::forward<Args>(args)...);
}

template<class T, auto... Sequences>
struct Foo
{
    void function_using_second_sequence()
    {
        constexpr auto vals = get_n<1>(Sequences...);
        for (auto i : vals) {
            std::cout << i << " ";
        }
        std::cout << '\n';
    }
};

struct Bar { };

int main()
{
    Foo<Bar, std::array{1, 2, 3, 4}, std::array{5, 6, 7}> foo;
    foo.function_using_second_sequence();
}

This is a simplified answer that assumes you only need 2 integer sequences, instead of a variadic amount of them as in @Mestkon's solution. This supports C++11 since std::integer_sequence was added in C++14. See on godbolt

#include <iostream>
#include <vector>

// std::integer_sequence is since C++14, and the OP requires C++11
template< class T, T... Ints >
class integer_sequence{};

void f(std::vector<int> const & vec1, std::vector<int> const & vec2) {
    for(auto val : vec1) {
        std::cout << val;
    }
    std::cout << " ";
    for(auto val : vec2) {
        std::cout << val;
    }
}

template <class Sequence1, class Sequence2>
struct Foo
{
    Foo() {
        call_f(Sequence1{}, Sequence2{});
    }
    template <class T1, T1 ...Ints1, class T2, T2 ...Ints2>
    void call_f(integer_sequence<T1, Ints1...>, integer_sequence<T2, Ints2...>) {
        f({Ints1...}, {Ints2...});
    }
};

int main() {
    // Prints 1234 567
    Foo<integer_sequence<int, 1, 2, 3, 4>, integer_sequence<int, 5, 6, 7>> foo;
    return 0;
}
Related