sazr
sazr

Reputation: 25928

Hash Not Defined for Class Reference?

I have a funny error occuring where I have a custom class I am using as keys in a std::unordered_map. I have implemented a hash function for this custom class.

If I create an std::unordered_map like so everything runs fine:

std::unordered_map <Component, int> myMap;

But if I create a std::unordered_map where the keys are references it complains there is no implemented hash function for this type:

std::unordered_map <Component&, int> myMap; 

Error 3 error C2338: The C++ Standard doesn't provide a hash for this type.

Any idea how I can get this to work?

class Component
{
public:
    GUID id;

    Component()
    { 
        generateGUID(&id);
    }

    bool operator== (const Component& other) const
    {
        return compareGUID(id, other.id);
    }
};

namespace std
{
    template<>
    struct hash<Component>
    {
        std::size_t operator()(const Component& cmp) const
        {
            using std::size_t;
            using std::hash;

            return hash<GUID>()(cmp.id); // GUID hash has also been implemented
        }
    };
}


std::unordered_map<Component, int> cMap1;  // compiles ok
std::unordered_map<Component&, int> cMap2;  // causes compiler error

Upvotes: 3

Views: 1447

Answers (1)

Jack
Jack

Reputation: 133577

The problem is not how to correctly override hash for Component&. The problem is that STL containers are meant to store objects and references are not objects. Mind that the term object includes primitives and pointers, since they require storage, while a reference does not.

To overcome your problem you should look into std::reference_wrapper, which is able to wrap a reference inside an object, then the code would become something like:

#include <iostream>
#include <unordered_map>
#include <functional>

using namespace std;

struct Component
{
    size_t id;
};

namespace std
{
    template<>
    struct hash<reference_wrapper<Component>>
    {
        size_t operator()(const reference_wrapper<Component>& cmp) const
        {
            return cmp.get().id;
        }
    };
}

unordered_map<reference_wrapper<Component>, int> mapping;

If you don't want to use reference_wrapper then you should use pointers (Component*) as keys.

Upvotes: 3

Related Questions