0

I am trying to write a hash function for an internal class, which is a protected member of a larger class. In addition, the hash function should use protected members of the internal class (in this case, a string). So this is what it looks like without the hash function:

class MasterClass
{
    public:
    // Blah blah blah
    protected:

        class InternalClass
        {
            public:
            // Blah blah blah 2
            protected:
                string m_Value;
        };

        unordered_map<InternalClass, uint> m_Example_Map;
};

Since I'm using InternalClass as a key in an unordered_map within MasterClass, I need to define the hash function.

I'm using the following references:

But I'm in over my head. My best guess is something like this:

class MasterClass::InternalClass;
namespace std
{
    template<> struct hash<MasterClass::InternalClass>
    {
    public:
        size_t operator()(const MasterClass::InternalClass& i_Internal) const;
    };
}

class MasterClass
{
    public:
    // Blah blah blah
    protected:

        class InternalClass
        {
            public:
            // Blah blah blah 2
            protected:
                string m_Value;
        };

        unordered_map<InternalClass, uint> m_Example_Map;

        friend struct std::hash<MasterClass::InternalClass>::operator()(const MasterClass::InternalClass& i_Name) const;
};

namespace std
{
    template<> size_t hash<MasterClass::InternalClass>::operator()(const MasterClass::InternalClass& i_Internal) const
    {
        return(std::hash<std::string>{}(*i_Internal.m_Value);
    }
}

However, this is riddled with compiler errors, including "Invalid friend declaration" and "explicit specialization of class "std::hash" must precede its first use (at line 719 of "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\include\type_traits")"

How can I define my hash function for the protected, internal class (using protected members of the internal class)?

user3236291
  • 133
  • 7

2 Answers2

1

I think this is what you want:

class MasterClass
{
public:
    // Blah blah blah
protected:

    class InternalClass; // Forward declaration needed for friend declaration

    class MyHash
    {
    public:
        size_t operator()(const MasterClass::InternalClass& i_Internal) const;
    };

    class InternalClass
    {
    public:
        // Blah blah blah 2
    protected:
        std::string m_Value;

        friend size_t MasterClass::MyHash::operator()(const MasterClass::InternalClass& i_Internal) const; // firend to allow access to m_value
    };

    std::unordered_map<InternalClass, unsigned, MyHash> m_Example_Map;
};

// Implementation of hash
size_t MasterClass::MyHash::operator()(const MasterClass::InternalClass& i_Internal) const
{
    return std::hash<std::string>{}(i_Internal.m_Value);
}

I'd also ask, why protected? protected makes items accessible to derived classes. You may have cut these out in the simplification, but if not you want to use private.

Jasper Kent
  • 3,546
  • 15
  • 21
0

I don't think there is any way of achieving this with std::hash, because you need to define the specialization of std::hash before you define MasterClass (because it needs to be at namespace scope and because the instantiation of m_Example_Map's type requires it) and you need to define MasterClass before you define the specialization because it needs the inner class type.

But std::unordered_map doesn't need to use std::hash. You can provide your own hash functor instead:

class MasterClass
{
    public:
    // Blah blah blah
    protected:

        class InternalClass
        {
            public:
                // Blah blah blah 2
                struct hash {
                    auto operator()(const InternalClass& v) const {
                        return std::hash<std::string>{}(v.m_Value);
                    }
                };
                bool operator==(const InternalClass& other) const {
                    return m_Value == other.m_Value;
                }
            protected:
                string m_Value;
        };

        unordered_map<InternalClass, uint, InternalClass::hash> m_Example_Map;
};
walnut
  • 21,629
  • 4
  • 23
  • 59