2015-07-13 19 views
10

Vorrei progettare una classe che crea tipi interni che sono varianti di tipi passati come parametri del modello. Qualcosa come il seguente esempio, non funzionale:Modifica dei parametri del modello di modello in 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> > 

Da Base è un tipo completamente specificato, Base<RefinedType> refinedStorage; mancheranno di compilazione. Semplicemente usando un parametro template template non funzionerà, poiché il tipo raffinato deve essere basato sul parametro del template annidato e sul suo tipo base.

Come è possibile implementare questo modello di creazione di tipi in base ai tipi completamente specificati e di base di un parametro di modello?

MODIFICA: mi piacerebbe che fosse un composito a profondità arbitraria, con più tipi di iniettori che eseguono una cascata di trasformazioni. Quindi, passare sia il parametro template template che il parametro base diventa piuttosto ingombrante (in particolare quando si tratta di trattare il caso base del composito), e una soluzione ideale userebbe la sintassi più diretta.

+1

Perché non vuoi renderlo un parametro di template _and_ parametro T, da utilizzare come 'iniettore '? – Petr

+0

@Petr Ci ho pensato, ma idealmente mi piacerebbe impostare questo come un composito di profondità arbitraria con modifiche che si propagano attraverso più livelli. –

risposta

10

sono stato in grado di raggiungere questo obiettivo in modo esplicito 'ri-dichiarando' il modello generale al suo interno:

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, questo è come ['std :: allocator'] (http://en.cppreference.com/w/cpp/memory/allocator) lo fa (tranne che lo chiamano' rebind', e il suo typedef interno è chiamato 'other'). – Barry

4

È possibile fornire un rebinder esterna:

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, ho appena notato che abbiamo la stessa risposta. Er. +1 suppongo :) – Barry

8

È potrebbe introdurre un modello 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; 

con cui la vostra Injector diventa:

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

Non v'è alcuna necessità di un modello di rilegatura, che l'eccesso di complica la situazione. È sufficiente un modello di modello di modello:

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

Dovrai utilizzare una specializzazione di modello per ottenere un tipo concreto da un modello di modello.

Questo è usato in questo modo:

using injector_int = Injector<Base<int>>; 

int main(){ 
    injector_int i; 
} 

here's a live example

+0

* che aggiunge solo al tempo di compilazione * => lo fa? Voglio dire c'è qualche effetto misurabile sulle prestazioni? Questa sembra essere un'affermazione priva di fondamento, che distrae dal punto principale: la soluzione è * più semplice * di una rebind, e semplice è meglio (in generale). –

+0

@MatthieuM. il tempo di compilazione non influisce sulle prestazioni, quindi non sono proprio sicuro di cosa intendi. Ma sì, l'aggiunta in un'altra classe per il rebinding dovrebbe in teoria far sì che il compilatore faccia più lavoro; anche se solo nell'analisi della fonte extra. – CoffeeandCode

+0

Stavo parlando di prestazioni di compilazione :) È solo che, per quanto mi riguarda, questo commento è abbastanza inutile e potrebbe sminuire il punto principale (la risposta stessa). –