2009-02-20 11 views
13

Sto tentando di dichiarare una e una classe Column, con il avere un privato std::map con i valori che punta a un template Column. Qualcosa di simile a questo:C++ std :: mappa di template di classe Valori

template <typename T> 
class DataType { 
    private: 
    T type; 
}; 
template <typename T> 
class Field { 
    private: 
    T value; 
    DataType<T> value; 
}; 
class Row { 
    private: 
    std::map<unsigned long,Field*> column; 
}; 

Bene, suppongo che in linea di principio la classe non avrebbe dovuto sapere che tipo di Field (o Column) vorremmo utilizzare, vale a dire che si tratti di un Field<int> nella colonna 1 o a Field<double> nella colonna 2. Ma non sono sicuro della sintassi corretta per la dichiarazione Row::column o se lo std::map è limitato in questo senso e dovrei utilizzare qualcos'altro.

Io approvo i vostri suggerimenti e vi ringrazio per loro in anticipo.

+0

Quindi qual è la domanda? –

+1

non devi trasformare il tuo codice in html. basta metterlo così com'è, con una rientranza di 4 caratteri. –

+0

a Dave: a Dave: La mia domanda è: Dal momento che il campo è un modello, come posso "dire" a std :: map che i valori sono "qualsiasi tipo di campo"? a litb: Grazie per il suggerimento! :-) – jbatista

risposta

22

Field da solo non è un tipo, ma un modello che può generare una famiglia di tipi, ad esempio Field<int> e Field<double>. Tutti questi campi non sono correlati in modo tale che l'uno sia in qualche modo derivato dall'altro o così. Quindi devi stabilire una relazione tra tutti questi tipi generati. Un modo è quello di utilizzare un non-modello di classe base comune:

class FieldBase { }; 

template <typename T> 
class Field : public FieldBase { 
    private: 
    T value; 
    DataType<T> type; 
}; 
class Row { 
    private: 
    std::map<unsigned long,FieldBase*> column; 
}; 

E considerare l'utilizzo di puntatore intelligente al posto di tale puntatore grezzo nel codice. Ad ogni modo, ora il problema è che le informazioni sul tipo sono perse - se si punta a un Field<double> oa un Field<int> non è più noto e può essere rilevato solo mantenendo una sorta di flag di tipo nella base che è impostato dal modello classe derivata - o chiedendo RTTI utilizzando

dynamic_cast<Field<int>*>(field) != 0 

Ma questo è brutto. Soprattutto perché quello che vuoi è un valore semantico. Io dovresti essere in grado di copiare la tua riga e copiare tutti i campi in essa contenuti. E si vorrebbe ottenere un doppio quando viene memorizzato un doppio - senza prima usare RTTI per accedere al tipo derivato.

Un modo per farlo è usare un sindacato discriminato. Questa è fondamentalmente un'unione per alcuni tipi arbitrari e in aggiunta un flag di tipo, che memorizza quale valore è attualmente memorizzato in quel campo (ad esempio se un double, int, ...). Ad esempio:

template <typename T> 
class Field { 
    private: 
    T value; 
    DataType<T> type; 
}; 
class Row { 
    private: 
    std::map<unsigned long, 
      boost::variant< Field<int>, Field<double> > > 
     column; 
}; 

boost :: variante fa tutto il lavoro per voi. Puoi usare visitation per farlo chiamare un functor usando il sovraccarico corretto.Date un'occhiata al suo manual

1
  1. C'è un errore lì: devi "valore" membro nel campo (uno dovrebbe probabilmente essere "tipo").
  2. Si prega di non mantenere puntatori grezzi nel valore della mappa. Utilizzare boost::shared_ptr.
  3. Inoltre, dovresti avere una buona ragione per scrivere tali classi dove ci sono già un sacco di codice per la gestione di tabelle/DB che puoi già usare. Quindi, se è applicabile, considera l'utilizzo di qualcosa di esistente e non scrivere il tuo codice di gestione della tabella.

Ora, per rispondere alla tua domanda :), le classi Field <> possono ereditare da una classe base comune condivisa da tutti i tipi di dati. In questo modo un contenitore come la tua mappa di colonne può mantenere puntatori (rendere quello puntatori condivisi) a oggetti derivati ​​che sono istanziati da una classe template.

+0

1. Scusa se hai ragione, intendevo: T avalore; DataType atype; 2. Non ho familiarità con la libreria Boost (suppongo sia di questo che stai parlando), ma ci penserò, grazie. 3. Potresti indicare uno o due suggerimenti che potrei esaminare? – jbatista

0

A Row< int, float, int> è molto diverso da uno Row<int, std::string>. Chiaramente, Row<int,float,int>.field<0> dovrebbe essere un Field<int> mentre Row<int,float,int>.field<1> dovrebbe essere un Field<float>. E Row<int,float,int>.field<3> è un errore del compilatore.

Il modo più semplice per farlo è utilizzare Boost. Un sacco di informazioni sono state sperimentate da Loki (vedi Modern C++ Design, by Andrei Alexandrescu) ma Boost è più moderno e meglio supportato.

Normalmente, non si itererà sui campi: ogni campo ha il proprio tipo. Ma di quello che fai, avresti davvero bisogno di un FieldBase. Se è necessaria una simile interfaccia, è probabilmente utile archiviare internamente i campi come boost::array<FieldBase, N> (ad esempio, Row<int,float,int> ha uno boost::array<FieldBase, 3>). Non dovresti mai aver bisogno di dynamic_cast che FieldBase*, però. Questo è un test di runtime e tu sai sempre l'esatto T di ogni Field<T> in fase di compilazione.