Specializing std::hash for private member class
Asked Answered
H

2

5

I have a class (call it Outer) which has a private member class (Inner). I want to store instances of Outer::Inner in unordered standard containers, so I want to specialize std::hash<Outer::Inner>.

However, when writing this:

namespace std {
    template<>
    struct hash<Outer::Inner> {
        std::size_t operator()(const Outer::Inner &arg) const
        {
            return std::hash<int>()(arg.someSpecialProperty);
        }
    };
}

the compiler complains:

error: 'Inner' is a private member of 'Outer'
            std::size_t operator()(const Outer::Inner &p) const
                                                ^

I have tried to make std::hash a friend struct by following this answer, but that didn't work either: the forward declaration of Outer::Inner failed with:

error: use of undeclared identifier 'Outer'

So how should I proceed (if what I intend to do is possible at all)?

Hertel answered 20/4, 2014 at 21:11 Comment(5)
By refering to this post: #8760681 it seems like your case is not possible, because you can't add a friendship in std::hash ...Confiscatory
@Confiscatory because I am not the one who declared the original std::hash template, right?Hertel
this is the reason, yes.Confiscatory
@Confiscatory Oh, thanks. Well, that's disappointing. :-( (don't hesitate to write an answer if you feel like that.)Hertel
Can't you just declare friend struct std::hash<Inner>; inside Outer? (Probably after declaring or defining Inner.) And then define your specialisation after.Miksen
B
5

Since it's a private inner type, I assume that you have a private or protected std::unordered_map member in the enclosing class. If that's the case, just write a private inner hash functor and pass it as the third argument of the std::unordered_map. It's the easiest solution to your problem, I think.

Bushed answered 20/4, 2014 at 22:7 Comment(1)
Yeah, I finally ended up doing this. Not the prettiest solution, I guess.Hertel
C
9

Got it ! The solution is to use your own functors, not specialize std::hash.

struct A
{
  A() { v.insert(std::make_pair(B(1), 6)); }

private:
  struct B
  {
    B(int i = 0) : m_i(i) { }

    int m_i;
  };

  struct HashB { std::size_t operator()(const B& b) const { return b.m_i; } };
  struct EqualB { bool operator()(const B&b1, const B&b2) const { return b1.m_i == b2.m_i; } };


  std::unordered_map<B, int, HashB, EqualB> v;
};
Confiscatory answered 20/4, 2014 at 22:7 Comment(0)
B
5

Since it's a private inner type, I assume that you have a private or protected std::unordered_map member in the enclosing class. If that's the case, just write a private inner hash functor and pass it as the third argument of the std::unordered_map. It's the easiest solution to your problem, I think.

Bushed answered 20/4, 2014 at 22:7 Comment(1)
Yeah, I finally ended up doing this. Not the prettiest solution, I guess.Hertel

© 2022 - 2024 — McMap. All rights reserved.