johnsam
johnsam

Reputation: 4572

error: invalid use of incomplete type 'struct std::hash<>'

How do I create a hash function for key as pair of string and enum to be used in unordered_map?

I have the following pair which is the key to an unordered map how do I create a hash so it can be used in unordered_map?

enum Color {
Red,
Green
};

typedef std::pair <std::string, Color>

I tried the following but got compilation error

struct EnumClassHash
{
    template <typename T>
    std::size_t operator()(T t) const
    {
        return static_cast<std::size_t>(t);
    }
};
size_t hash( const PairType & pair ) {
    return std::hash<std::string>()(pair.first) ^
           std::hash<EnumClassHash>()(pair.second);
}

typedef std::unordered_map<PairType, std::string>, 
    std::function<decltype(hash)>> ColorMapType;
error: invalid use of incomplete type 'struct std::hash<EnumClassHash>'
            std::hash<EnumClassHash>()(pairType.second);

The following isnt working either

size_t hash( const PairType & pairType ) {
    return std::hash<std::string>()(pairType.first) ^
           std::hash<Color>()(pairType.second);
}


 typedef std::unordered_map<PairType, std::string, \
    std::function<decltype(hash)>> ColorMapType;

ColorMapType colorMap(100, hash);
error: invalid use of incomplete type 'struct std::hash<Color>'
            std::hash<Color>()(pair.second

);

PS Pardon my formatting. I lost my gmail password and can only post from mobile app which is a tiny screen.

Upvotes: 1

Views: 2793

Answers (1)

user2486888
user2486888

Reputation:

  • When you declare struct EnumClassHash, it is its own thing, not a specialization of std::hash.
  • When you write std::hash<EnumClassHash>()(pair.second), the template specialization std::hash<EnumClassHash> simply doesn't exist.

You can either:

  • declare struct EnumClassHash, and use EnumClassHash, or
  • specialize std::hash into std::hash<Color>, and use std::hash<Color>, or
  • do both,

but never mix and match part of each method, which does not make any kind of sense.

Also, to use std::pair<std::string, Color> as the key type of std::unordered_map, neither std::hash<Color> nor EnumClassHash matters. What matters is std::hash<std::pair<std::string, Color>> or your own class that hashes std::pair<std::string, Color>.


Here is a recipe you can follow.

To use std::pair<std::string, Color> as key type, you need to specialize std::hash<std::pair<std::string, Color>> yourself.

template <>
struct std::hash<std::pair<std::string, Color>>
{
    std::size_t operator()(const std::pair<std::string, Color>& p) const {
        return std::hash<std::string>()(p.first) ^ std::hash<Color>()(p.second);
    }
};

But before that, you need to specialize std::hash<Color> first since std::hash<std::pair<std::string, Color>> uses it.

template <>
struct std::hash<Color>
{
    std::size_t operator()(Color c) const {
        return static_cast<size_t>(c);
    }
};

Then, you can use something like std::unordered_map<std::pair<std::string, Color>, std::string>. No EnumClassHash involved.


If you love EnumClassHash very much, here is another recipe.

You can keep EnumClassHash as you like.

struct EnumClassHash
{
    template <typename T>
    std::size_t operator()(T t) const
    {
        return static_cast<std::size_t>(t);
    }
};

This time, std::hash<Color> does not exist. Don't use it. (Not to mention std::hash<EnumClassHash> which does not make sense.)

So, the class that hash the key type should use EnumClassHash. (Once again, not std::hash<EnumClassHash>!)

struct PairHash {
    std::size_t operator()(const std::pair<std::string, Color>& p) const {
        return std::hash<std::string>()(p.first) ^ EnumClassHash()(p.second);
    }
};

Now, you can use std::unordered_map<std::pair<std::string, Color>, std::string, PairHash>.

Upvotes: 3

Related Questions