0

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?

Yury Finchenko
  • 1,035
  • 13
  • 19
  • You also need an `operator==` to check for equality. Hashing on it's own is not enough. Since hashing collision is a thing, the standard implementation uses an equality comparison on top of the hash. – super Mar 27 '20 at 10:54
  • If you don't have the equal operator how would you check if you actually found the correct record and not just one that has the same hash? – Surt Mar 27 '20 at 10:55

1 Answers1

1

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);
        }
    };
};
Tau
  • 496
  • 4
  • 22
  • Seems a bit round-about to implement a specialization for `equal_to` when `operator==` would do just fine. That's what `equal_to` falls back to if there is no available specialization. – super Mar 27 '20 at 11:10
  • Of course. Just thought that it fits nicely to the above hash operator and seperates concerns if a different `operator==` behaviour is needed. In hindsight, I agree that's not very likely in this case, but in the general case, one may for example want to store things in a set by reference but unique them by their values. – Tau Mar 27 '20 at 11:13
  • Sure, there is probably use-cases for it if you look hard enough. Chances are that you break things in for example the `` header if you do that. Expecting `operator==` to be used when in fact your specialization kicks in instead. – super Mar 27 '20 at 13:28