Search code examples
c++hashstructure

hash function in c++ for a custom type


I try to write the some custom function than allow to use my custom structure in the unordered_set template.

I have the structure:

struct test_record {
    int value;
    std::string name;
};

And the hash operator:

namespace std {

    template<> struct hash<test_record> {

        using argument_type = test_record;
        using result_type = size_t;

        size_t operator()(const test_record& r) const {

            const std::hash<std::string> str_hash_fn;
            const std::hash<int> int_hash_fn;

            const size_t result = str_hash_fn(r.name) ^ int_hash_fn(r.value);

            return result;
        }
    };
};

Use it like:

int main()
{

    std::unordered_set <test_record> myrecordsset;

    myrecordsset.insert({ 1, "one" }); // error!

}

But the compiler throws the error message:

1>E:\Development\Visual Studio\VC\Tools\MSVC\14.25.28610\include\xstddef(91,22): error C2676: binary '==': 'const _Ty' does not define this operator or a conversion to a type acceptable to the predefined operator 1> with 1>
[ 1> _Ty=test_record 1> ] 1>E:\Development\Visual Studio\VC\Tools\MSVC\14.25.28610\include\xstddef(90): message : while compiling class template member function 'bool std::equal_to::operator ()(const _Ty &,const _Ty &) const' 1> with 1> [ 1> _Ty=test_record 1>
] 1>E:\Development\Visual Studio\VC\Tools\MSVC\14.25.28610\include\xhash(164): message : see reference to function template instantiation 'bool std::equal_to::operator ()(const _Ty &,const _Ty &) const' being compiled 1> with 1> [ 1>
_Ty=test_record 1> ] 1>E:\Development\Visual Studio\VC\Tools\MSVC\14.25.28610\include\xmemory(1318): message : see reference to class template instantiation 'std::equal_to' being compiled 1>E:\Development\Visual Studio\VC\Tools\MSVC\14.25.28610\include\xmemory(1318): message : see reference to variable template 'const bool is_empty_v >' being compiled 1>E:\Development\Visual Studio\VC\Tools\MSVC\14.25.28610\include\unordered_set(30): message : see reference to class template instantiation 'std::_Uhash_compare<_Kty,_Hasher,_Keyeq>' being compiled 1>
with 1> [ 1> _Kty=test_record, 1>
_Hasher=std::hash, 1> _Keyeq=std::equal_to 1> ] 1>E:\Development\Visual Studio\VC\Tools\MSVC\14.25.28610\include\xhash(342): message : see reference to class template instantiation 'std::_Uset_traits<_Kty,std::_Uhash_compare<_Kty,_Hasher,_Keyeq>,_Alloc,false>' being compiled 1> with 1> [ 1>
_Kty=test_record, 1> _Hasher=std::hash, 1> _Keyeq=std::equal_to, 1> _Alloc=std::allocator 1> ] 1>E:\Development\Visual Studio\VC\Tools\MSVC\14.25.28610\include\unordered_set(65): message : see reference to class template instantiation 'std::_Hash,_Alloc,false>>' being compiled 1> with 1> [ 1>
_Kty=test_record, 1> _Hasher=std::hash, 1> _Keyeq=std::equal_to, 1> _Alloc=std::allocator 1> ] 1>E:\Development_Projects\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.cpp(20): message : see reference to class template instantiation 'std::unordered_set,std::equal_to,std::allocator>' being compiled

What wrong with it? Why the compiler require the equal_to operator in a unordered_set?


Solution

  • The point of a set is that it contatins no two identical objects. Identity cannot be determined though by just looking at the hash, so you need a concrete equality comparasion. C++ requires that you explicitly define equality for your class in case you need any special behaviour.

    Adding this should do the trick:

    namespace std {
        template<> struct equal_to<test_record> {
            using argument_type = test_record;
            using result_type = bool;
            constexpr bool operator()(const test_record &lhs, const test_record &rhs) const {
                return (lhs.name == rhs.name) && (lhs.value == rhs.value);
            }
        };
    };