2015-07-13 26 views
10

Chciałbym zaprojektować klasę, która tworzy typy wewnętrzne, które są wariantami typów przekazywanych jako parametry szablonu. Coś jak następujący, niefunkcjonalnych przykład:Zmiana parametrów szablonu szablonu w C++

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> > 

Od Base jest w pełni określony typ, Base<RefinedType> refinedStorage; nie uda się skompilować. Po prostu użycie parametru szablonu szablonu nie będzie działać, ponieważ zaawansowany typ musi być oparty na parametrze zagnieżdżonego szablonu, jak również na jego typie podstawowym.

W jaki sposób mogę zaimplementować ten wzorzec tworzenia typów na podstawie w pełni określonych i podstawowych typów parametru szablonu?

EDYCJA: Chciałbym, aby był to układ złożony z dowolnych głębokości, z wieloma typami wtryskiwaczy wykonującymi kaskadę transformacji. Zatem przekazanie zarówno parametru szablonu szablonu, jak i podstawowego parametru staje się dość nieporęczne (szczególnie gdy chodzi o radzenie sobie z podstawowym przypadkiem kompozytu), a idealne rozwiązanie korzystałoby z bardziej bezpośredniej składni.

+1

Dlaczego nie zrobisz to parametr szablonu szablon _i_ parametr T, które mają być użyte jako 'Injector '? – Petr

+0

@Petr Zastanowiłem się nad tym, ale najlepiej byłoby ustawić to jako kompozycję o dowolnej głębokości, ze zmianami rozprzestrzeniającymi się przez wiele warstw. –

Odpowiedz

10

udało mi się to osiągnąć poprzez wyraźne 'ponownie deklarując' ogólny szablon wewnątrz siebie:

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> > 
+4

FWIW, tak robi się ['std :: allocator'] (http://en.cppreference.com/w/cpp/memory/allocator) (z wyjątkiem tego, że nazywa je' rebind', a jego wewnętrzny typedef jest nazywany "inne"). – Barry

4

można zapewnić rebinder zewnętrzne:

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; 
}; 

[Live example]

+0

Ha, właśnie zauważyłem, że mamy tę samą odpowiedź. Er. +1 Zgaduję :) – Barry

8

Ty może wprowadzić szablon 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; 

z jaką Injector staje:

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

Nie ma potrzeby ponownego wiązania do szablonu, który nadmiernie komplikuje sytuację. Po prostu wybierz szablon szablonu:

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; 
}; 

Będziesz musiał użyć specjalizacji szablonu, aby uzyskać konkretny typ z szablonu szablonu.

Służy tak:

using injector_int = Injector<Base<int>>; 

int main(){ 
    injector_int i; 
} 

here's a live example

+0

* który po prostu dodaje czas kompilacji * => robi to? Chodzi mi o to, czy istnieje jakiś wymierny wpływ na wydajność? Wydaje się, że jest to niepotwierdzone twierdzenie, które odrywa od głównego punktu: twoje rozwiązanie jest * prostsze * niż ponowne wiązanie, a proste jest lepsze (ogólnie). –

+0

@MatthieuM. czas kompilacji nie ma wpływu na wydajność, więc nie jestem pewien, co masz na myśli. Ale tak, dodanie innej klasy do ponownego wiązania powinno teoretycznie spowodować, że kompilator wykona więcej pracy; nawet jeśli analizowane jest dodatkowe źródło. – CoffeeandCode

+0

Mówiłem o wydajności kompilacji :) Chodzi o to, że o ile mi chodzi, ten komentarz jest dość niepotrzebny i może umniejszać główny punkt (sama odpowiedź). –

Powiązane problemy