14

Is it possible to do mixing of types and nontypes in variadic template parameters? If I were to pass a std::array for instance to this class as parameter T, I would need to also pass a type for the array and a length, but the way I tried it below causes an error when encountering a value, because it only expects types for Types:

template <
    template<class, std::size_t>  class T,
    class ... Types>
class C {

    T<Types...> storage;
};

int main(){
    C<std::array, int, 3> c;
}

Error message:

error: template argument for template type parameter must be a
      type
    Container<std::array, int, 3> c;
                               ^

Is there a way to pass types and values in a variadic context?

lo tolmencre
  • 3,804
  • 3
  • 30
  • 60

2 Answers2

6

Is it possible to do mixing of types and nontypes in variadic template parameters?

No. You can't mix and match. But since you can wrap a value in a type but not the other way around, you can just stay in the world of types:

template <template<class...> class T, class ... Types>
class C {    
    T<Types...> storage;
};

And then it's just a matter of making std::array work with just types:

template <class T, class N>
using my_array = std::array<T, N::value>;

template <size_t N>
using size_ = std::integral_constant<size_t, N>;

So your original example becomes:

C<my_array, int, size_<3>> c;
Barry
  • 286,269
  • 29
  • 621
  • 977
  • "you can wrap a value in a type but not the other way around" -- it seems being worth to note that passing types wrapped into specifically typed values as template parameters has become available as of c++20, as valid non-type template parameters have been redefined as instances of any literal type. – Géza Török Dec 19 '19 at 01:14
  • @GézaTörök I'm not sure what advantage that would offer in this scenario - where we need all the arguments to have the same kind. – Barry Dec 19 '19 at 15:07
3

As I see, you alreadty hardcoded the number and types of parameter the class T must take as template parameter. You don't need variadic templates here. Just do this instead:

template <
    template<class, std::size_t>  class T,
    class A, std::size_t N>
class C {

    T<A, N> storage;
};

int main(){
    C<std::array, int, 3> c; // works!
}

If you wish to use variadic templates, then put it in the template template parameter too:

template <
    template<typename...>  class T,
    typename... Types>
class C {

    T<Types...> storage;
};

If you wish to use that version but still want to use std::array, you can create an alias to std::array that already has a size:

template<typename T>
using array3 = std::array<T, 3>;

C<array3, int> c;

Alternatively, you can also create some sort of template template alias that let you choose the size:

template<std::size_t n>
struct sized_array {
    template<typename T>
    using array = std::array<T, n>;
};

C<sized_array<5>::array, int>;
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141