2013-10-03 14 views
6

Sto provando ad implementare una mappa con chiavi di accesso differenti utilizzando modelli variadic in C++. Quello che voglio ottenere è quello di rendere tale lavoro sintassi:Mappa multipla con modelli variadici

MultikeyMap<int, double, float> map1; // int and double are keys, float is value type 
map1[ 2 ] = 3.5; 
map1[ 5.7 ] = 22; 

MultikeyMap<unsigned long long, int, float, double, int> map2; // more keys, int is value type 
map2[100000000000ULL] = 56; 

// etc... 

Quello che ho ora assomiglia:

template<class V, class... Krest> 
class MultikeyMap; 

template<class V, class K, class... Krest> 
class MultikeyMap<V, K, Krest...> : protected std::map<K, V>, 
            protected MultikeyMap<V, Krest...> 
{ 
public: 
    template<class T> 
    void insert(const T& t, const V& v) 
    { 
     std::map<T, V>::insert(make_pair(t, v)); 
    } 

    template<class T> 
    const V* find(const T& k) 
    { 
     typedef std::map<T,V> currentMap; 
     currentMap::const_iterator it = currentMap::find(k); 
     return it == currentMap::end() ? 0 : &it->second; 
    } 

}; 

template<class V> 
class MultikeyMap<V> 
{}; 

non ho ritorno iteratori in insert e trovare per rendere il codice semplice.

Vedo due principali difetti in questa soluzione.

Prima il, il tipo di valore va prima nell'elenco degli argomenti del modello. Inizialmente ho provato a scrivere

template<class K, class... Krest, class V> 
class MultikeyMap<K, Krest..., V> 

ma compilatore insiste che "se un argomento per un modello di classe di specializzazione parziale è un pacchetto di espansione Essa è l'ultimo argomento".

Secondo è l'ereditarietà protetta da std :: maps. Mi piacerebbe molto usare la composizione al posto di quella, ma in quel caso non vedo un modo per accedere alle mappe memorizzate. Se ci fosse un static_if, vorrei scrivere

template<class V, class K, class... Krest> 
class MultikeyMap<V, K, Krest...> : protected MultikeyMap<V, Krest...> 
{ 
public: 
    template<class T> 
    void insert(const T& t, const V& v) 
    { 
     static if(is_same<T,K>::value) 
      m_map.insert(make_pair(t, v)); 
     else 
      MultikeyMap<V, Krest...>::insert(t, v); 
    } 
private: 
    std::map<K,V> m_map; 
}; 

consiglio prega sui problemi che ho citato. Se c'è un approccio migliore, sarò lieto di imparare.

Grazie per la lettura.

+0

'MultikeyMap ':' int' è citato come chiave due volte, è davvero inteso? Come farai a sapere se l'utente sta guardando dal primo o dal secondo 'int'? Inoltre, non è chiaro per me cosa stai cercando di raggiungere; dovrebbe essere presente un valore in tutte le mappe contemporaneamente? Perché se è solo su una mappa non ha senso avere un grande contenitore comune. –

+0

@MatthieuM .: Grazie per il punto, ci dovrebbe essere un int. Il valore dovrebbe essere presente in una mappa per ora, ma in seguito aggiungerò la possibilità di aggiungere un'altra chiave per il valore già memorizzato. –

risposta

2

Gli approcci più semplici ma non del tutto equivalenti sono probabilmente Boost.Bimap o Boost.MultiIndex.

Il primo è una mappa con cui chiavi possono occhiata valori e viceversa, mentre la seconda è molto più generale: si tratta di un contenitore con un numero arbitrario di indici, permettendo sia sequenziato ("lista-like"), casuale -access ("vector-like"), associativo ("simile alla mappa") e accesso hash.

Si potrebbe provare a racchiudere i modelli variadic attorno a Boost.MultiIndex, quindi almeno non è necessario reimplementare tutta la logica di inserimento/cancellazione (ma solo i wrapper sottili).

Nota: Boost.MultiIndex non richiede una sequenza variadic di tipi, si può anche avere una sequenza variadic di funzioni membro estrazione vari membri di dati di una classe definita dall'utente come tipo di dati primari.

2

Ecco come lo farei:

template<class V, class K, class... Krest> 
class MultikeyMap : MultikeyMap<V, Krest...>, 
        MultikeyMap<V, K> 
{ 
    using ParentMM = MultikeyMap<V, Krest...>; 
    using Parent = MultikeyMap<V, K>; 
public: 
    using ParentMM::insert; 
    using Parent::insert; 

    using ParentMM::find; 
    using Parent::find; 

    using ParentMM::operator[]; 
    using Parent::operator[]; 
}; 

template<class V, class K> 
class MultikeyMap<V, K> 
{ 
    std::map<K, V> k_map; 
public: 
    void insert(const K& k, const V& v) 
    { 
     k_map.insert(std::make_pair(k, v)); 
    } 

    const V* find(const K& k) const 
    { 
     auto it = k_map.find(k); 
     if (it != k_map.end()) 
      return &it->second; 
     return nullptr; 
    } 

    V& operator[](const K& k) 
    { 
     return k_map[k]; 
    } 
}; 

Ereditarietà sembra opportuno qui, in quanto sta combinando il comportamento di più implementazioni. Ho reso le basi private perché la dichiarazione using è necessaria in entrambi i casi per rendere visibili i membri. Solo il caso base ha un std::map come membro.

Non ho intenzione di disturbare invertire gli argomenti del modello, è lo stesso trucco utilizzato per std::tuple, basta cercare l'implementazione STL preferita.

EDIT

Ecco lo stesso codice, con il cambiamento banale ho già detto, in modo da chiavi arrivato primo nei parametri di tipo:

template<class Head, class... Tail> 
struct Helper : Helper<Tail...> { 
    using Last = typename Helper<Tail...>::Last; 
}; 

template<class T> 
struct Helper<T> { 
    using Last = T; 
}; 


template<class K, class... Rest> 
class MultikeyMap : MultikeyMap<Rest...>, 
        MultikeyMap<K, typename Helper<Rest...>::Last> 
{ 
    using ParentMM = MultikeyMap<Rest...>; 
    using Parent = MultikeyMap<K, typename Helper<Rest...>::Last>; 

public: 
    using ParentMM::insert; 
    using Parent::insert; 

    using ParentMM::find; 
    using Parent::find; 

    using ParentMM::operator[]; 
    using Parent::operator[]; 
}; 

template<class K, class V> 
class MultikeyMap<K, V> 
{ 
    std::map<K, V> k_map; 
public: 
    void insert(const K& k, const V& v) 
    { 
     k_map.insert(std::make_pair(k, v)); 
    } 

    const V* find(const K& k) const 
    { 
     auto it = k_map.find(k); 
     if (it != k_map.end()) 
      return &it->second; 
     return nullptr; 
    } 

    V& operator[](const K& k) 
    { 
     return k_map[k]; 
    } 
};