dtell
dtell

Reputation: 2568

Partial template deduction in template argument list

I have the following structs:

template<typename T1, typename T2>
struct A {};

template<typename T>
struct B {};

template<typename A, typename B>
struct C {};

and want to use them as follows:

C<B<int>, A<double, B<int>>> c;

Is there any way to deduce the second template parameter for A, such that I can use it like this?

C<B<int>, A<double>> c;

This should work for any template arguments of C and not just for a particular one (therefore default arguments don't seem to work).

Furthermore a solution for variadic templates would be even better, so instead of

C<B<int>, A<double, B<int>>, A<float, A<double, B<int>>>> c;

something like this would be nice:

C<B<int>, A<double>, A<float>> c;

Upvotes: 2

Views: 114

Answers (2)

SergeyA
SergeyA

Reputation: 62583

With a bit of template metaprogramming fun, I was able to solve your problem both for the two-parameter and the variadic case (although in a somewhat narrow way):

// TypeList stuff

template<class... Args>
struct List {
    template<class Arg>
    using Add = List<Args..., Arg>;
};

template<class List> struct TailI;
template<class A, class... Ar> struct TailI<List<A, Ar...>> {
    using type = typename TailI<List<Ar...>>::type;
};

template<class A> struct TailI<List<A>> {
    using type = A;
};


template<class List>
using Tail = typename TailI<List>::type;

template<template<class...> class OP, class List> struct rename_impl;
template<template<class...> class OP, class... ListArgs>
struct rename_impl<OP, List<ListArgs...>> {
    using type = OP<ListArgs...>;
};

template<template<class...> class OP, class List>
using rename = typename rename_impl<OP, List>::type;

// Actual code solving problem at hand    
template<class T1, class T2> struct A{};

template<class... Args> struct C{};

template<class Built, class Next, class... Rest>
struct builder {
    using NewBuilt = typename Built::template Add<A<Next, Tail<Built>>>;
    using type = typename builder<NewBuilt, Rest...>::type;
};

template<class Built, class Next>
struct builder<Built, Next> {
    using NewBuilt = typename Built::template Add<A<Next, Tail<Built>>>;
    using type = rename<C, NewBuilt>;
};


template<class First, class... Rest>
using c_builder = typename builder<List<First>, Rest...>::type;

using t = c_builder<int, double, float>;

// Test driver
#include <utility>

static_assert(std::is_same_v<t, C<int, A<double, int>, A<float, A<double, int>>>>, "");

Upvotes: 3

R Sahu
R Sahu

Reputation: 206607

For the simpler case of

template<typename A, typename B> struct C {};

you can use a helper class to provide you the type you need.

template<typename T1, typename T2>
struct C_Helper
{
   using type = C<B<T1>, A<T2, B<T1>>>;
};

and use

using C_Type = typename C_Helper<int, double>::type;
C_Type c;

I haven't thought through how that can be extended to support a variadic class template C,

Upvotes: 2

Related Questions