Specializing std::hash for templated Key
Asked Answered
K

1

7

I was trying to specialize hash for my own type, a templated key.

I was basing it off cppreference.

I get the compile error "The C++ Standard doesn't provide a hash for this type". I figure I just did it wrong. Could the compiler even support this kind of template?

namespace std {
    template<typename SType, typename AType, typename PType>
    struct MyKey {
        const SType from;
        const AType consume;
        const PType pop;
    };

    template<typename SType, typename AType, typename PType>
    struct hash<MyKey<SType, AType, PType>> {
        size_t operator ()(MyKey const &key) {
            std::hash<SType>()(key.from);
            std::hash<AType>()(key.consume);
            std::hash<PType>()(key.pop);
        }
    };
}
Kuching answered 27/4, 2015 at 10:23 Comment(1)
You have to teach the compiler how to hash your types - not just MyKey, but SType. AType and PType too (at least those that're not typedefs for known types). After you've done that, MyKey's hash shouldn't just call the member variable's hash functions, it should return a hash_combined value incorporating those member variables' hashes. There are lots of existing questions explaining this.Ephah
P
4

There are a few issues with your code:

You are not allowed to place new definitions or declarations into the std namespace; only specializations (such as std::hash) are allowed. So your MyKey template should be moved out of the std namespace.

Your operator() signature is incorrect. MyKey doesn't name a type, you need to explicitly paramaterise it. Additionally, the operator should be marked const.

std::hash specializations should provide the member types argument_type and result_type.

If there are not existing specializations for the types passed in as SType etc., you need to provide them yourself.

You are not returning anything from your hash function, just computing the hashes of the other types and throwing their return values away.

An implementation which will compile for types which have their own std::hash specialization:

//moved out of std
template<typename SType, typename AType, typename PType>
struct MyKey {
    const SType from;
    const AType consume;
    const PType pop;
};

namespace std {
    template<typename SType, typename AType, typename PType>
    struct hash<MyKey<SType, AType, PType>>{
        //member types
        using argument_type = MyKey<SType,AType,PType>;
        //arguments specified         ^     ^     ^
        using result_type = std::size_t;

        result_type operator ()(argument_type const& key) const {
        //marked const                                      ^
            //these will fail if SType and friends don't have a std::hash specialization
            result_type s_hash = std::hash<SType>()(key.from);
            result_type a_hash = std::hash<AType>()(key.consume);
            result_type p_hash = std::hash<PType>()(key.pop);

            //in your actual code, you'll want to compute the return type from the above
            return p_hash;
        }
    };
}
Penmanship answered 27/4, 2015 at 10:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.