1

I want to use unordered map with a custom Key class:

#include <iostream>
#include <unordered_map>
    
using namespace std;
    
class Item 
{ 
private: 
    std::string m_ItemId; 
    int m_Price;
    int m_Count;
    
public: 
    Item(std::string id, int price, int count): m_ItemId(id), m_Count(count), m_Price(price){} 
    int getCount() const { return m_Count;} 
    std::string getItemId() const { return m_ItemId;} 
    int getPrice() const { return m_Price;}
};
    
class Key 
{ 
    int m_value; 
public: 
    Key(int val) :m_value(val){} 
    int getValue() const {return m_value;} 
};
    
struct Compare 
{ 
    size_t operator()(const Key& a, const Key& b) const 
    { 
        return a.getValue() < b.getValue(); 
    }
};
    
unordered_map<Key, Item, Compare> getItemList() 
{ 
    unordered_map<Key, Item,Compare> mapOfItems ; 
    mapOfItems.insert(std::make_pair(Key(1), Item("D121",100,2)));  
    mapOfItems.insert(std::make_pair(Key(8), Item("D122",12,5)));    
    mapOfItems.insert(std::make_pair(Key(6), Item("D125",99,3)));    
    mapOfItems.insert(std::make_pair(Key(3), Item("D123",28,6)));     
    mapOfItems.insert(std::make_pair(Key(2), Item("D125",99,3))); 
    return mapOfItems; 
}
    
int main() 
{
    unordered_map<Key, Item, Compare> mp = getItemList();
    return 0;
}

I have a compilation error :

error: static assertion failed: hash function must be invocable with an argument of key type

Can you help me, please?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Ghazi
  • 13
  • 4
  • 2
    You must define a hash function for your class `Key`, otherwise use `std::map`. – 273K Mar 30 '23 at 16:47
  • 1
    `std::unordered_map` uses a hash, not `operator <` to determine where to place items. – PaulMcKenzie Mar 30 '23 at 16:54
  • 2
    `unordered_map` is not ordered (the hint is in the name), so an ordering relation won't do much good. You need a hashing function and an equality. (Or `std::map`, which *is* ordered.) – molbdnilo Mar 30 '23 at 16:54
  • This may help: https://stackoverflow.com/questions/18837857/cant-use-enum-class-as-unordered-map-key – Mikel F Mar 30 '23 at 17:09

1 Answers1

2

The thing to understand is that std::unordered_map is what the standard library calls hash tables. It's an "unordered" container in the sense that it is not a comparison-based associative container, such as a red-black tree which typical implementations of std::map are.

Its third template parameter is a hash function object not a comparison function. (Its fourth template parameter however is a comparison-like function object, an equality checking function, because hash table implementations need to test for equality to resolve hash collisions, etc.) As below:

// ... 
struct KeyHasher
{
    size_t operator()(const Key& a) const
    {
        return std::hash<int>{}(a.getValue());
    }
};

struct KeyEquals
{
    bool operator()(const Key& a, const Key& b) const
    {
        return a.getValue() == b.getValue();
    }
};

unordered_map<Key, Item, KeyHasher, KeyEquals> getItemList()
{
    unordered_map<Key, Item, KeyHasher, KeyEquals> mapOfItems;
    mapOfItems.insert(std::make_pair(Key(1), Item("D121", 100, 2)));
    mapOfItems.insert(std::make_pair(Key(8), Item("D122", 12, 5)));
    mapOfItems.insert(std::make_pair(Key(6), Item("D125", 99, 3)));
    mapOfItems.insert(std::make_pair(Key(3), Item("D123", 28, 6)));
    mapOfItems.insert(std::make_pair(Key(2), Item("D125", 99, 3)));
    return mapOfItems;
}

int main()
{
    unordered_map<Key, Item, KeyHasher, KeyEquals> mp = getItemList();
    return 0;
}
jwezorek
  • 8,592
  • 1
  • 29
  • 46