C++ variadic templates with type and non-type argument mixing for recursive inheritance

Viewed 163

I would like to achieve this:

Foo<int, 5, double, 7> foo_1;
Foo<char, 7> foo_2;

foo_1.Foo<int>::value[4] = 1;
foo_2.Foo<char>::value[1] = 'x';

(This is an oversimplified example, Foo would do much more than this.)

How can I do that with variadic templates?

TLDR;

I know that variadic templates can be used in this way if exclusively types or non-types are used:

template <typename ...T>
struct Foo;

template<typename T, typename ...Args>
struct Foo<T, Args...> : Foo<T>, Foo<Args...> {
    Foo(T t, Args... args) : Foo<T>(t), Foo<Args...>(args...) { }
};

template<typename T>
struct Foo<T> {
    T value;

    Foo<T>(T v) : value(v) {}
};

Foo<int, double, char> f(7, 88.1, 'x');
f.Foo<double>::value = 5;

But I do not whether it is possible to pair and mix type and non-type template arguments using variadic templates.

3 Answers

It's not possible. You'll have to settle for one of the following:

Foo<Bar<int, 5>, Bar<double, 7>> foo_1;
Foo<int, Bar<5>, double, Bar<7>> foo_1;
// ...?

If the values are always integral, you could also try this:

Foo<int[5], double[7]> foo_1;

And then extract elemenet types & extents from each argument.

But I do not whether it is possible to pair and mix type and non-type template arguments using variadic templates.

Not with an alternate variadic template. But you can wrap couples of types/sizes.

Suppose you have a Bar class as follows

template <typename T, std::size_t N>
struct Bar
 { std::array<T, N>  value; };

using class specialization you can have a variadic Bar so a variadic list of types and a variadic list of sizes

template <typename...>
struct Foo;

template <typename ... Ts, std::size_t ... Ns>
struct Foo<Bar<Ts, Ns>...> : public Bar<Ts, Ns>...
 { };

You can use it as follows

   Foo<Bar<int, 5>, Bar<double, 7>> foo_1;
   Foo<Bar<char, 7>> foo_2;

   foo_1.Bar<int, 5>::value[4] = 1;
   foo_2.Bar<char, 7>::value[1] = 'x';

The following is a full compiling example

#include <array>

template <typename T, std::size_t N>
struct Bar
 { std::array<T, N>  value; };

template <typename...>
struct Foo;

template <typename ... Ts, std::size_t ... Ns>
struct Foo<Bar<Ts, Ns>...> : public Bar<Ts, Ns>...
 {
 };

int main ()
 {
   Foo<Bar<int, 5>, Bar<double, 7>> foo_1;
   Foo<Bar<char, 7>> foo_2;

   foo_1.Bar<int, 5>::value[4] = 1;
   foo_2.Bar<char, 7>::value[1] = 'x';
 }

You can pass array type as typename, example in your case will become:

Foo<std::array<int, 5>, std::array<double, 7> > foo_1(
    std::array<int, 5>{0,0,0,0,0},
    std::array<double, 7>{0,0,0,0,0,0,0});

foo_1.Foo<std::array<int, 5>>::value[4] = 1;
Related