3

I have the following two structs:

template<typename T>
struct one { /* ... */ };

template<template<typename...> typename T>
struct two { /* ... */ };

When I have example instantiated/uninstantiated templates like this:

template<typename T>
struct sample_templated { /* ... */ };

using instantiated = sample_templated<double>;

then I can do

one<instantiated>{};
two<sample_templated>{};

just fine. I would like to merge the definition of one and two such that they have the same name, though, as this would allow recursion.

I tried having a default definition like

template<typename...>
struct types_match_impl;

and having the two original structs be partial specializations of this, but this is incompatible with two.

What is the solution here?

alter_igel
  • 6,899
  • 3
  • 21
  • 40
Post Self
  • 1,471
  • 2
  • 14
  • 34
  • It is unclear exactly what you want, but I am guessing you want to pass `types_match_impl` to itself. Can you show the code you would like to write, and describe what you think it should do? – jxh Apr 17 '19 at 23:18

1 Answers1

3

The way you're hoping for is not possible. Here's why:

one<instantiated>{};
two<sample_templated>{};

one "uses" more than two: It is referring to instantiated which is sample_templated instantiated with double. two on the other hand is only "using" the sample_templated.

When you think of templates as functions on types, then this becomes even clearer: two is a function which accepts a (type-level) function to create some type. one is a function which accepts a type to create some type:

one :: T -> one<T>
two :: (T -> U) -> two<(T -> U)>

Put differently the parameter of one has a different "kind" ("type of type") as the one of two.

What you can do:

  • You can provide a specialization of one which accepts a template template parameter ("type level function") and the template parameter for that:

    template<template<typename...> typename TT, typename T>
    struct one<TT<T>>  { /* ... */ }; // this basically "calls" TT
    
  • You can turn two into something which can accept both, albeit with a "dummy" template template parameter:

    template<template<typename...> typename TT, typename... Ts>
    struct two { /* */ };
    
    template<typename...>
    struct Void;
    
    template<typename T>
    struct two<Void<>, T> { /* Use T like in one */ };
    // or derive from one<T>
    

There are probably more approaches, but these will depend on your specific use case.

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
  • Thank you, I understand. The solution looks promising, I'll get back with my results (PS: love the Haskell thrown in :) ) – Post Self Apr 18 '19 at 05:46
  • Unfortunately it is not possible to have both "kinds" of types in the same variadic parameter pack, so recursion as I have imagined it, is not possible – Post Self Apr 18 '19 at 06:22
  • 1
    It turns out that for my design I don't even need uninstanciated templates, but this information will be helpful for the future – Post Self Apr 18 '19 at 10:43