4

When doing

unordered_map<pair<unsigned int, unsigned int>, unsigned int> m; 

we get

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

Is there a built-in way to define a hash for a std::pair of int or do we need to define it manually? (in this case, the hash could be just (the bytes of first item)(the bytes of second item of the pair) glued together).

Note: I'm using VC++ 2013.

Note2: the answer to pair<int,int> pair as key of unordered_map issue doesn't clearly address the question of how to actually create the hash with two ints, as detailed here.

2
  • 2
    If you are asking is it part of the C++ Standard, then no. Commented Jul 30, 2017 at 0:11
  • There is no "built-in way"; however you are free to define and implement your own specialization. Commented Jul 30, 2017 at 0:18

1 Answer 1

5

If you don't want to use boost, rolling your own shouldn't be too hard. static_assert added to ensure the assumption that 2 ints fit into 1 size_t is maintained.

using IntPair = std::pair<int, int>; struct IntPairHash { static_assert(sizeof(int) * 2 == sizeof(size_t)); size_t operator()(IntPair p) const noexcept { return size_t(p.first) << 32 | p.second; } }; std::unordered_map<IntPair, int, IntPairHash> myMap; 
Sign up to request clarification or add additional context in comments.

4 Comments

I don't understand the downvote on this answer. It's not required to specialize std::hash; that's just an option.
In fact, I would recommend to avoid specializing std::hash in a case like this one because it affects a larger scope of code than just your one use-case. Don't specialize std::hash when you're a CLIENT of a user-defined type that you want to hash, but when you are the PROVIDER of a type, and you want everyone to use a sensible hash function for your type. Specializing for std::hash on a standard type increases the chance to conflict with someone else's specialization and potentially violate the ODR.
If p.second is negative and being implicitly casted to size_t, its binary representation may be all 1 at the high 32 bits, thus masking p.first, which may cause a conflict. So it may be necessary to do something like size_t(p.second) & 0xFFFFFFFF.
Very good point @hakula. The next issue is assuming the size of int. I put a static_assert that 2 ints fit in a size_t, but it might be better to use a fixed size integer (std::int32_t, etc) to be sure it fits. The mask depends on the int being 4 bytes. We could alternate cast it to std::uint32_t (again assuming we know the size) before ANDing it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.