3

I want to overload the std::hash template for my custom type called AnimationSet:

struct AnimationSet {
    const AnimationData *animationData;
    SceneNode *sceneNode;

    bool operator==(const AnimationSet &other) const {
        return ( this->animationData == other.animationData &&
                 this->sceneNode == other.sceneNode );
    }
};

As you can see, it's a struct that contains just two pointers.

Is it legit to cast those pointers to unsigned int in order to calculate the hash for AnimationSet?

namespace std {
    template<>
    struct hash<AnimationSet> {
        size_t operator()(const AnimationSet &set) const {
            hash<unsigned int> h;

            return h((unsigned int)set.animationData) ^ h((unsigned int)set.sceneNode);
        }
    };
}

Edit: I'm asking this question in the context of hash overloading, but I'd like to know the answer to the more general question: "Is it fair to cast any pointer to unsigned int?"

McLovin
  • 3,295
  • 7
  • 32
  • 67
  • For your "general question", please refer to [this](http://stackoverflow.com/questions/4574745/is-it-safe-to-assume-that-a-pointer-is-the-size-of-an-int-in-c). – Alejandro Aug 23 '15 at 15:33
  • 3
    `std::hash` is already specialised for pointers, so you don't need to convert in order to hash. You can directly use `hash` and `hash`. – Alan Stokes Aug 23 '15 at 15:39

2 Answers2

8

In general, no, a pointer is not necessarily the same size as an unsigned int. In particular, on most 64-bit systems they will be twice as large, and so all you will do is take the least significant 32-bits of the pointer, which is more likely to lead to collisions (especially as many pointers will not have the lowest two bits set, so you're only getting 30 bits of useful information contributing to the hash value).

You should #include <cstdint> and cast to std::uintptr_t instead. That's an unsigned integer type that is guaranteed to be able to store the value of a pointer without losing any bits.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
3

While it's true that a pointer is really just an unsigned integer value (at least on modern PC-like systems), it's not certain that you can use unsigned int for it. Remember that the size of int and the size of a pointer may differ (e.g. on 64-bit systems).

If you want an integer type that is big enough to fit a pointer use uintptr_t from <cstdint>.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 1
    A pointer isn't always *just* an unsigned integer value; C++ works with oddball architectures like the old segmented 16-bit x86, and there's always member pointers to consider. – Mark Ransom Aug 23 '15 at 15:47