2013-08-28 16 views
6

Diciamo che ho uno std::map<std::string, T> (o unordered_map) e voglio accedere alla chiave da un iteratore/riferimento/puntatore al contenuto.Evitare la memorizzazione duplicata della chiave della mappa

C'è un modo per farlo senza avere due copie della chiave std::string (una di proprietà della mappa, una all'interno dell'oggetto contenuto)? Si può essere un riferimento all'altro?

+1

Come usare un 'std :: set ' dove 'C' confronta la stringa memorizzata in' T'? –

+0

@Daniel: Ma poi avrei bisogno di fornire un intero oggetto 'T' per la ricerca, e non solo un' std :: string', giusto? Oppure 'C' può essere sovraccarico per confrontare' T' con 'T' e anche' T' per 'std :: string'? –

+0

Ah, vedo che C++ 14 aggiungerà un membro 'find' basato su template per cercare su qualsiasi tipo che possa comparare con' T'. –

risposta

-1

Entrambi possono essere un riferimento allo stesso valore. Per esempio:

#include <stdio.h> 
#include <string> 
#include <map> 

struct xx { std::string mykey; int value; }; 

int main (int argc, char **argv) 
{ 
    std::string      key1("the-key"); 
    std::map<std::string, xx>  map; 

    map[key1].mykey = key1; 
    map[key1].value = 13; 

    std::string &lookup_key = map[key1].mykey; 

    printf("%d\n", map[lookup_key].value); 
} 
+0

Penso che tu abbia frainteso la domanda. Se ho 'auto & content = map [key1];', devo essere in grado di passare di nuovo da 'content' a' key1'. Un modo è inserire una copia della chiave all'interno del tipo di contenuto. Sto chiedendo se c'è un modo per evitare di avere una copia. –

+0

Oh, questo lo chiarisce. L'unico modo sarebbe quello di percorrere l'intera raccolta per trovare il contenuto con lo stesso indirizzo. Oppure mantieni una seconda raccolta che dia la mappa per indirizzo o altro contenuto di T. – ash

+0

A proposito, è questo che mi porta a rispondere come ho fatto io: "Si può essere un riferimento all'altro?" – ash

1

Perché non creare due oggetti:

std::set<std::string> wordSet; 
std::map<std::string*, T> yourMap; 

T deve contenere puntatore a std :: string, e yourMap ha bisogno di confronto personalizzato. Inoltre puoi avvolgere tutto questo in qualche classe.

3

Prenderesti in considerazione l'utilizzo di boost :: bimap? Di seguito è riportato un semplice esempio:

#include <boost/bimap.hpp> 
#include <string> 
struct Person 
{ 
    Person() 
    {} 
    Person(const std::string& f, const std::string& l, int a) : first(f), last(l), age(a) 
    {} 
    std::string first; 
    std::string last; 
    int age; 
}; 

bool operator <(const Person& lhs, const Person& rhs) 
{ 
    if(lhs.last < rhs.last) 
     return true; 
    return false; 
} 

std::ostream& operator << (std::ostream& os, const Person& p) 
{ 
    os << "First Name: " << p.first << " Last Name: " << p.last << " Age: " << p.age; 
    return os; 
} 

int main() 
{ 
    typedef boost::bimap<std::string, Person> people; 
    typedef people::value_type value; 

    people m; 
    m.insert(value("12345",Person("fred", "rabbit", 10))); 
    m.insert(value("67890",Person("benjamin", "bunny", 12))); 

    Person p = m.left.at("12345"); 
    std::cout << "Person with serial no. 12345 is: " << p << "\n"; 
    std::cout << "Serial number of " << p << " is: " << m.right.at(p) << "\n"; 

} 
+0

Potrebbe funzionare, ma mi sembra che sia progettato per il caso in cui si ha solo una copia dell'elemento 'mapped_type', probabilmente sintetizzato, senza metadati come un puntatore diretto. –

2

Il motivo per cui l'hanno reso difficile è perché è pericoloso. È necessario GARANTIRE che nessuno dei membri std::string con chiave disattivata non cambierà mai valore, o l'intera mappa non sarà più valida. Interessante, la prima soluzione che mi viene in mente sembra follemente hacker, e sembra come UB, ma credo che scrupolosamente segua l'UB.

struct key_type { 
    mutable const char* ptr;  
}; 
bool operator<(const key_type& lhs, const key_type& rhs) 
{return strcmp(lhs.ptr, rhs.ptr)<0;} 

struct person { 
    std::string name; 
    int age; 
}; 
person& people_map_get(std::map<key_type, person>& map, const char* name) { 
    auto it = map.insert(name, person{name}).first; //grab, possibly insert 
    if->first.ptr = it->second.name.c_str(); //in case of insert, fix ptr 
    return it->second; 
} 
person& people_map_assign(std::map<key_type, person>& map, person p) { 
    auto pair = map.insert(name, p); //grab, possibly insert 
    auto it = pair.first;  
    if (pair.second == false) 
     it->second = std::move(p); 
    if->first.ptr = it->second.name.c_str(); //ptr probably invalidated, so update it 
    return it->second; 
} 

int main() { 
    std::map<key_type, person> people; 
    people_map_assign(people, person{"ted"}); 
    person frank = people_map_get(people, "frank"); 
} 

Come spero sia chiaro, questo è pazzo vicino al UB, e molto non è raccomandato. Fondamentalmente, durante un insert/find, i punti chiave sul tuo oggetto temporaneo o sulla stringa di input, e quindi non appena l'oggetto viene inserito/trovato, la chiave viene cambiata in modo che punti al valore contenuto nel membro della stringa, e fino a quando non fai mai nulla che invalida il valore di ritorno di .c_str() su qualsiasi oggetto person contenuto, tutto funziona a malapena. Credo.

+0

Come sottolineato da Dribeas, posso mantenere un 'std :: pair *' e avere accesso ad entrambi. Altrimenti, come spiegato da Dribeas, la chiave ha una posizione di memoria fissa, quindi il contenuto potrebbe memorizzare un puntatore a quella chiave. Penso che sarebbe molto meno pazzo. –

+0

Non penso che tu possa memorizzarlo in 'T', poiché' T' sarebbe incompleto al punto di dichiarazione, e 'pair' lo richiede per essere completo. Nessuna attesa, perché un puntatore a "coppia" potrebbe non averne bisogno ... Non sono sicuro che funzioni o meno. Potrebbe. –

+0

Posso sicuramente mantenere un 'const std :: string *' in 'T'. Vedo il tuo punto sul fatto di inserire 'value_type *' o iterator all'interno di un altro elemento, ma non ho bisogno che 'std :: pair' sia completo lì, poiché sto semplicemente memorizzando un puntatore. –

Problemi correlati