prestokeys
prestokeys

Reputation: 4849

Specifying a default template

Consider this:

template <typename Pack, template <typename...> class = std::tuple> struct foo;

template <template <typename...> class P, typename... Ts, template <typename...> class Q>
struct foo<P<Ts...>, Q> {
    using type = Q<P<Ts>...>;   
};

I've placed the default template in the typedef to be std::tuple to make this compile, but what I actually want is the default template to be P, and I don't know of any syntax that allows this. I kept the typedef simple to illustrate the point, which is trying to get P as the default template. So I came up with the following workaround that seems kind of ugly:

template <typename Pack, template <typename...> class...> struct foo;

template <template <typename...> class P, typename... Ts, template <typename...> class Q>
struct foo<P<Ts...>, Q> {
    using type = Q<P<Ts>...>;   
};

template <template <typename...> class P, typename... Ts>
struct foo<P<Ts...>> {
    using type = P<P<Ts>...>;   
};

Is there any better way to do it than this? Perhaps some c++17 syntax that I'm not aware of?

Upvotes: 3

Views: 95

Answers (2)

max66
max66

Reputation: 66200

In this case, using (combined with template) is your friend.

You need a type-traits to extract the container in Pack, by example

template <typename>
struct bar;

template <template <typename...> class P, typename ... Ts>
struct bar<P<Ts...>>
 {
   template <typename ... Us>
   using templ_type = P<Us...>;
 };

so you can extract the default value, for the second template parameter, from Pack as follows

template <typename Pack,
          template <typename...> class = bar<Pack>::template templ_type>
struct foo;

The following is a full compiling example

#include <type_traits>

template <typename ...>
struct baz
 { };

template <typename>
struct bar;

template <template <typename...> class P, typename ... Ts>
struct bar<P<Ts...>>
 {
   template <typename ... Us>
   using templ_type = P<Us...>;
 };

template <typename Pack,
          template <typename...> class = bar<Pack>::template templ_type>
struct foo;

template <template <typename...> class P, typename... Ts,
          template <typename...> class Q>
struct foo<P<Ts...>, Q>
 { using type = Q<P<Ts>...>; };

int main()
 {
   foo<baz<short, int, long>>  f0;

   static_assert( std::is_same<decltype(f0)::type,
      baz<baz<short>, baz<int>, baz<long>>>{}, "!" );
 }

Upvotes: 2

Barry
Barry

Reputation: 302708

If you're looking for a different default that's an identity, you can create such a thing:

template <class... I> struct foo_id_impl;
template <template <class...> class P, class... Ts>
struct foo_id_impl<P<Ts>...> { using type = P<P<Ts>...>; };

template <class... Id>
using foo_id = typename foo_id_impl<Id...>::type;

template <class Pack, template <class...> class = foo_id>
struct foo;

I'm not sure that's necessarily better than your solution, but maybe if need this in multiple places.

Upvotes: 1

Related Questions