18

I have a class C, which has a string* ps private data member.
Now, I'd like to have an unordered_map<C, int> for which I need a custom hash function.

According to the c++ reference, I can do that like

namespace std {
  template<>
  class hash<C> {
  public:
    size_t operator()(const C &c) const
    {
      return std::hash<std::string>()(*c.ps);
    }
  };
}

The problem is that I can't seem to make operator() and C friends so that I could access ps.

I have tried this:

class C;
template<>
class std::hash<C>;
class C{
  //...
  friend std::hash<C>::operator ()(const C&) const; // error: Incomplete type 
};
// define hash<C> here.

but it says that Incomplete type ... in nested name specifier ...

I can't turn around the definitions either, because if class C is defined later, the hash<C> has no way to know about ps.

What am I doing wrong here? How can this situation be fixed without making ps public?

Barney Szabolcs
  • 11,846
  • 12
  • 66
  • 91

3 Answers3

25

Try this:

class C;
namespace std {
  template<>
  struct hash<C> {
  public:
    size_t operator()(const C &c) const; // don't define yet
  };
}
class C{
  //...
  friend size_t std::hash<C>::operator ()(const C&) const;
};
namespace std {
  template<>
  size_t hash<C>::operator()(const C &c) const {
    return std::hash<std::string>()(*c.ps);
  }
}

Or this:

class C;
template<>
struct std::hash<C>;
class C{
  friend struct std::hash<C>; // friend the class, not the member function
};

(I haven't compiled so there might be a syntax error)

Richard
  • 56,349
  • 34
  • 180
  • 251
Pubby
  • 51,882
  • 13
  • 139
  • 180
  • yes, that's correct, thx, I'll mark it accepted in a sec. In the mean time I also figured out how silly I was! [embarrassed] :) – Barney Szabolcs Dec 15 '12 at 09:02
  • 4
    for msvc2012, you'll need to remove `template<>` in the implementation of the size_t `hash::operator()`, otherwise `error C2910 cannot be explicitly specialized`. – Jichao Jul 02 '13 at 03:18
  • +1 for Jichao. On Gcc without removing `template<>`, I get `error: template-id ‘operator()<>’ for ‘std::size_t std::hash::operator()(const C&) const’ does not match any template declaration` – Conchylicultor Jan 22 '17 at 22:26
0

Simpler way to make friends:

class C {
  template <typename T> friend class std::hash;
};

This works even when C is in its own namespace or is a nested class, as std::hash won't have to be forward declared.

ungato
  • 153
  • 2
  • 12
-2

I'd suggest to add method like following

class C
{
....
public:  const string* get_ps() const { return ps; }
....
};

and use it in the your hash specialization.

ivan.ukr
  • 2,853
  • 1
  • 23
  • 41