5

I have to use unordered_set for a rather large project, and to make sure I was using it correctly I tried a small example.

#include <iostream>
#include <unordered_set>
using namespace std;

class Foo {
  private:
    int x;
  public:
    Foo(int in) {x = in;}
    bool operator==(const Foo& foo) const {return x == foo.x;}
    size_t hash(const Foo& foo) const {return x;}
};

int main() {
  Foo f1(3);
  unordered_set<Foo> s;
  s.insert(f1);
  return 0;
}

When I compile I get:

/tmp/cc3KFIf4.o: In function `std::__detail::_Hash_code_base<Foo, Foo, std::_Identity<Foo>, std::equal_to<Foo>, std::hash<Foo>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>::_M_hash_code(Foo const&) const':
hashset.cc:(.text._ZNKSt8__detail15_Hash_code_baseI3FooS1_St9_IdentityIS1_ESt8equal_toIS1_ESt4hashIS1_ENS_18_Mod_range_hashingENS_20_Default_ranged_hashELb0EE12_M_hash_codeERKS1_[std::__detail::_Hash_code_base<Foo, Foo, std::_Identity<Foo>, std::equal_to<Foo>, std::hash<Foo>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>::_M_hash_code(Foo const&) const]+0x19): undefined reference to `std::hash<Foo>::operator()(Foo) const'
/tmp/cc3KFIf4.o: In function `std::__detail::_Hash_code_base<Foo, Foo, std::_Identity<Foo>, std::equal_to<Foo>, std::hash<Foo>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>::_M_bucket_index(std::__detail::_Hash_node<Foo, false> const*, unsigned int) const':
hashset.cc:(.text._ZNKSt8__detail15_Hash_code_baseI3FooS1_St9_IdentityIS1_ESt8equal_toIS1_ESt4hashIS1_ENS_18_Mod_range_hashingENS_20_Default_ranged_hashELb0EE15_M_bucket_indexEPKNS_10_Hash_nodeIS1_Lb0EEEj[std::__detail::_Hash_code_base<Foo, Foo, std::_Identity<Foo>, std::equal_to<Foo>, std::hash<Foo>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>::_M_bucket_index(std::__detail::_Hash_node<Foo, false> const*, unsigned int) const]+0x28): undefined reference to `std::hash<Foo>::operator()(Foo) const'
collect2: ld returned 1 exit status

It seems like it's not seeing my hash function, but I thought "hash" was the default function name. Did I define hash correctly? Or do I need to explicitly declare a separate hash class as the second template argument?

scottmsul
  • 89
  • 1
  • 2
  • 5
  • 1
    The hash functionality is passed as an extra template parameter. You need (preferably) a functor and do `unordered_set s(FooHasher());` – Xeo Aug 03 '12 at 20:44

1 Answers1

8

As an alternative to Xeo's suggestion in the comments, you can provide a specialization for std::hash before you instantiate the unordered_set.

namespace std {
    template <>
    struct hash<Foo> {
        size_t operator () (const Foo &f) const { return f.hash(f); }
    };
}

The foo parameter to your hash method seems extraneous to me, but I implemented the specialization to the interface you provided.

jxh
  • 64,506
  • 7
  • 96
  • 165
  • Ok thanks, that worked. I actually didn't for my hash function to take an extra parameter, that is a typo. It should be size_t hash() const {return x;} The only thing I'm confused about is why can't I just make a hash() like I did with operator==()? For operator== I didn't have to do anything extra. – scottmsul Aug 03 '12 at 22:07
  • @user1575106: Different APIs will have different requirements. The standards writers cannot cater to everyone's desire, and the given interface makes it easy to try different hash algorithms without modifying the class. – jxh Aug 03 '12 at 22:17