skypjack
skypjack

Reputation: 50540

Template template and partial specialization: a puzzle

Consider the following code:

template<typename>
struct S { };

template<typename, typename>
struct B;

template <typename R, typename... Args, template<class> class C>
struct B<R(Args...), C<R>> {
    void f() { }
};

int main() {
    B<void(), S<void>> b;
    b.f();
}

It compiles and has no problem.
Anyway, whenever one decides to use B, it has to provide two types.
What I'd like to achieve is to default somehow the second parameter (I know, partial specializations do not accept a default for their parameters) and let an user define it's type as B<void()> instead of B<void(), S<void>>.
Unfortunately, because of template template, partial specialization and the dependency existent between the parameters, all together they lead to a puzzle against which I'm struggling since a couple of hours.

Is there any clever solution to do that?
So far, I have been able to solve it with intermediate structures, but I don't like it so much...

Upvotes: 1

Views: 84

Answers (3)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275385

Here we change B into a template alias.

B_t does the default arg work.

B_impl is the implementation of B without any default args.

B is a using alias that gets the result of B_t.

template<class> struct S {};

template<class, class>
struct B_impl;

template<class R, class... Args, template<class...> class C>
struct B_impl<R(Args...), C<R>> {
  void f() { }
};

template<class, class=void>
struct B_t;

template<class R, class...Args>
struct B_t<R(Args...), void>:
  B_t<R(Args...),S<R>>
{};

template<class R, class... Args, template<class...> class C>
struct B_t<R(Args...), C<R>> {
  using type=B_impl<R(Args...), C<R>>;
};

template<class Sig, class Z=void>
using B=typename B_t<Sig,Z>::type;

The downside is that pattern-matching on B won't work well.

Upvotes: 1

Barry
Barry

Reputation: 303007

Partial specializations don't accept default parameters, but the primary does. You can just add it there:

template<typename Sig, typename X = S<return_type_t<Sig>>>
struct B;

Then all you need to do is implement a return type metafunction for a signature. Something like:

template <class Sig>
struct return_type;

template <class Sig>
using return_type_t = typename return_type<Sig>::type;

template <class R, class... Args>
struct return_type<R(Args...)> {
    using type = R;
};

Upvotes: 2

Jarod42
Jarod42

Reputation: 217275

You may create an helper class for that:

template <typename T> struct default_arg;

template <typename R, typename... Args>
struct default_arg<R(Args...)>
{
    using type = S<R>;
};

template<typename Sign, typename T = typename default_arg<Sign>::type>
struct B;

Demo

Upvotes: 2

Related Questions