Sobes.tech
Middle

Cosa bisogna fare per memorizzare la propria struttura in un unordered_map?

sobes.tech AI

Risposta dell'AI

Per memorizzare una struttura come chiave in std::unordered_map, è necessario fornire:

  1. Funzione di hash (hash function): Trova il valore hash per un'istanza della struttura.
  2. Operatore di confronto di uguaglianza (operator==): Confronta due istanze della struttura per verificare se sono uguali.

Ci sono diversi modi per fornire questi elementi:

  • Sovraccaricare operator== all'interno della struttura e specializzare std::hash per la tua struttura. Questo è l'approccio più comune e raccomandato.

    #include <unordered_map>
    #include <string>
    #include <functional>
    
    struct MyStruct {
        int id;
        std::string name;
    
        bool operator==(const MyStruct& other) const {
            return id == other.id && name == other.name;
        }
    };
    
    // Specializzazione di std::hash per MyStruct
    namespace std {
        template <>
        struct hash<MyStruct> {
            size_t operator()(const MyStruct& obj) const {
                size_t h1 = hash<int>()(obj.id);
                size_t h2 = hash<std::string>()(obj.name);
                return h1 ^ (h2 << 1);
            }
        };
    }
    
    // Esempio di utilizzo in main
    int main() {
        std::unordered_map<MyStruct, int> my_map;
        MyStruct key1 = {1, "Alice"};
        MyStruct key2 = {2, "Bob"};
        MyStruct key3 = {1, "Alice"}; // equivalente a key1
    
        my_map[key1] = 10;
        my_map[key2] = 20;
    
        if (my_map.count(key3)) {
            // my_map[key3] restituirà 10
        }
    
        return 0;
    }
    
  • Fornire funzioni di hash e confronto come parametri separati nella dichiarazione di unordered_map. Meno comodo se si utilizza frequentemente la struttura come chiave, poiché è necessario specificare i tipi di comparatore e hash ad ogni dichiarazione del contenitore.

    #include <unordered_map>
    #include <string>
    #include <functional>
    
    struct MyStruct {
        int id;
        std::string name;
    };
    
    // Funzione di hash separata
    struct MyStructHash {
        size_t operator()(const MyStruct& obj) const {
            size_t h1 = std::hash<int>()(obj.id);
            size_t h2 = std::hash<std::string>()(obj.name);
            return h1 ^ (h2 << 1);
        }
    };
    
    // Funzione di confronto separata
    struct MyStructEqual {
        bool operator()(const MyStruct& lhs, const MyStruct& rhs) const {
            return lhs.id == rhs.id && lhs.name == rhs.name;
        }
    };
    
    // Esempio di utilizzo in main
    int main() {
        std::unordered_map<MyStruct, int, MyStructHash, MyStructEqual> my_map;
        MyStruct key1 = {1, "Alice"};
        MyStruct key2 = {2, "Bob"};
    
        my_map[key1] = 10;
        my_map[key2] = 20;
    
        return 0;
    }
    

Punti importanti:

  • Correttezza della funzione di hash: Per ogni coppia di chiavi a e b, se a == b, allora hash(a) deve essere uguale a hash(b). Il contrario non è richiesto (possono esserci collisioni).
  • Qualità della funzione di hash: Una buona funzione di hash distribuisce uniformemente i valori di hash per diverse chiavi, minimizzando le collisioni e migliorando le prestazioni (O(1) in media).
  • Constanza: Gli operatori di confronto e la funzione di hash devono essere const, poiché non devono modificare l'oggetto chiave.