Изменение параметров шаблона шаблона в С++

Я хотел бы создать класс, который создает внутренние типы, которые являются вариантами типов, передаваемых в качестве параметров шаблона. Что-то вроде следующего, нефункционального примера:

template <typename T>
class BaseClass
{
public:
    typedef T InternalType;
    std::vector<InternalType> storage;
};

template <typename Base>
class Injector
{
public:
    typedef std::pair<typename Base::InternalType, typename Base::InternalType> RefinedType;
    Base<RefinedType> refinedStorage;
};

typedef Injector<BaseClass<int> > InjectedInt; // Should store vector<pair<int, int> >

Так как Base является полностью заданным типом, Base<RefinedType> refinedStorage; не скомпилируется. Просто использование шаблона шаблона не будет работать, так как уточненный тип должен быть основан на вложенном параметре шаблона, а также на его базовом типе.

Как я могу реализовать этот шаблон создания типов на основе как полностью заданных, так и базовых типов параметра шаблона?

EDIT: Я хотел бы, чтобы это был композит с произвольной глубиной, с несколькими типами инжекторов, выполняющими каскад преобразований. Таким образом, передача параметра шаблона шаблона и базового параметра становится довольно громоздкой (особенно когда дело касается базового случая композита), и идеальное решение будет использовать более прямой синтаксис.

Ответ 1

Я смог добиться этого, явно "повторно объявив" общий шаблон внутри себя:

template <typename T>
class BaseClass
{
public:
    typedef T InternalType;
    std::vector<InternalType> storage;

    template<class T2>
    using Recur = BaseClass<T2>;
};

template <typename Base>
class Injector
{
public:
    typedef std::pair<typename Base::InternalType, typename Base::InternalType> RefinedType;
    typename Base::template Recur<RefinedType> refinedStorage;
};

typedef Injector<BaseClass<int> > InjectedInt; // Should store vector<pair<int, int> >

Ответ 2

Вы можете ввести шаблон rebind:

template <typename From, typename To>
struct rebind_1st;

template <template <typename... > class Cls, typename A0, typename... Args, typename To>
struct rebind_1st<Cls<A0, Args...>, To> {
    using type = Cls<To, Args...>;
};

template <typename From, typename To>
using rebind_1st_t = typename rebind_1st<From, To>::type;

С помощью вашего Injector станет:

template <typename Base>
class Injector
{
public:
    typedef std::pair<typename Base::InternalType, 
                      typename Base::InternalType> RefinedType;
    rebind_1st_t<Base, RefinedType> refinedStorage;
};

Ответ 3

Нет необходимости в повторном шаблоне, что усложняет ситуацию. Просто введите шаблон шаблона:

template<typename>
struct Injector;

template<typename T, template<typename> class Base>
struct Injector<Base<T>>{
    using refined_type = std::pair<typename Base::InternalType, typename Base::InternalType>;
    Base<refined_type> refined_storage;
};

Вам нужно будет использовать специализированную специализацию, чтобы получить конкретный тип из шаблона шаблона.

Это используется так:

using injector_int = Injector<Base<int>>;

int main(){
    injector_int i;
}

вот живой пример

Ответ 4

Вы можете предоставить внешний rebinder:

template <class Bound, class U>
struct rebinder;

template <template <class> class Binder, class B, class U>
struct rebinder<Binder<B>, U>
{
  typedef Binder<U> type;
};

// Usage:

template <typename Base>
class Injector
{
public:
    typedef std::pair<typename Base::InternalType, typename Base::InternalType> RefinedType;
    typename rebinder<Base, RefinedType>::type refinedStorage;
};

[Живой пример]