objectnabb
objectnabb

Reputation: 63

Using hash directly in methods from unordered_map instead of the user defined object producing the hash

So i have created a class that i use as a hash in an unordered_map

class MyClass
{
  hash_type mHash = 0;
  hash_type hash() { return mHash; }

  bool operator!= (const MyClass& rhs) const;
  bool operator== (const MyClass& rhs) const;
}
namespace std
{
    template <>
    struct hash<MyClass>
    {
        hash_type operator()(const MyClass& k) const noexcept
        {
            return k.hash();
        }
    };
}

It works as expected but i would like to add some functionality. I would like to be able to use the hash itself when using unordered_map functions like find and erase. Now i have to do this:

void _erase_key(const MyClass& key) { umap.erase(key); }

But i would like to be able to do this as well:

void _erase_key(const hash_type key) { umap.erase(key); }

Is it possible to somehow use the hash directly instead of the object producing the hash when using methods like find and erase?

Upvotes: 0

Views: 132

Answers (1)

n314159
n314159

Reputation: 5085

If I understand you right, you want to have an std::unordered_map<MyClass, Value> such that you can also query with hash_type and you have hash_type h == MyClass m if std::hash<MyClass>{}(m) == h. Is this correct?

This is not possible in C++17. With C++20, there will be added the functionality of transparent hashes. You can read about that very briefly here. With this, your map has to fulfill certain properties

  • Your equality type Eq must provide a member type Eq::is_transparent, i.e. you have to put a using is_transparent = some_type; in it. (The exact type is without consequence.)
  • An object of type Eq has to provide an overload to compare all possible combinations of types you want to use. I.e. provide overloads for (MyClass, MyClass) and (MyClass, hash_type).
  • Your hash type Hash has to provide a member type Hash::transparent_key_equal, so again, put using transparent_key_equal = some_type; in it.
  • An object of type Hash must be callable with every type you want to use. I.e. you have to have an operator() overload for both MyClass and hash_type.

For Eq you can use std::equal_to<> (note the empty diamond!) if you provide publically accessible operator== for the appropriate types. This is NOT the default for unordered_map.

There is to the best of my knowledge no analagon for std::hash, so you have to add an own type for that and provide it to the map.

Pre-C++20 the only thing you can do if you want to keep your key type is to write a conversion from hash_type to MyClass.

But I sense a fundamental problem in this, namely that you see two objects of MyClass as identical if they have the same hash. If this is unintentional, you should really fix this. If it is intentional, make sure, that operator==(MyClass, MyClass) also only compares hashes.

In the later case, there is an easy fix for your problem. Change your map to std::unordered_map<hash_type, Value> and hash each MyClass you use to query the map. If you need a reverse lookup to get MyClass from a hash, either write a conversion function from hash_type to MyClass if this is possible, otherwise add another std::unordered_map<hash_type, MyClass>, where you store every object of type MyClass you ever use.

Upvotes: 3

Related Questions