Hash Function for User Defined Class. How to Make Friends?

Hash function for user defined class. How to make friends? :)

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)

C++: Defining Hash Function for Nested Internal, Protected Class

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;
};

Specializing std::hash for private member class

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.

std:hash with access to private members of a class

What you can do is declare std::hash<Foo> as a friend of Foo:

class Foo {
private:
std::string a;
std::string b;

public:
Foo(std::string a, std::string b);
bool operator==(const Foo& other) const;
bool operator!=(const Foo& other) const;
std::size_t operator()(const Foo&) const;

friend std::hash<Foo>;
};

Can a friend class create objects from its friend classes in C++?

This looks like a circular include.

  • "Hash_class.h" includes "remove_duplicates.h"
  • "remove_duplicates.h" includes "Hash_class.h"

To fix it, remove this line from "Hash_class.h":

#include "remove_duplicates.h"

That should work because the line

friend class remove_duplicates;

requires no information about how class remove_duplicates is implemented.

EDIT: To avoid further errors, wrap both header files in include guards.

How to implement a good __hash__ function in python

__hash__ should return the same value for objects that are equal. It also shouldn't change over the lifetime of the object; generally you only implement it for immutable objects.

A trivial implementation would be to just return 0. This is always correct, but performs badly.

Your solution, returning the hash of a tuple of properties, is good. But note that you don't need to list all properties that you compare in __eq__ in the tuple. If some property usually has the same value for inequal objects, just leave it out. Don't make the hash computation any more expensive than it needs to be.

Edit: I would recommend against using xor to mix hashes in general. When two different properties have the same value, they will have the same hash, and with xor these will cancel eachother out. Tuples use a more complex calculation to mix hashes, see tuplehash in tupleobject.c.

Why do I have to define a hash function for each namespace as the unordered_set?

You can reduce the clutter a long way, by using Boost's ADL-enabled customization point hash_value:

class Vec {
private:
std::vector<int> v;

friend size_t hash_value(const Vec& v) {
return boost::hash_range(begin(v.v), end(v.v));
}
friend bool operator==(const Vec& lhs, const Vec& rhs) {
return lhs.v == rhs.v;
}
};

In fact, the hash function can be even simpler with return boost::hash_value(v.v); in this case.

This is already enough to make Boost's unordered containers work with your type:

boost::unordered_set<Vec> s2;
s2.insert(v);

Adding std support

That's a non-issue now:

template <> struct std::hash<Vec> : boost::hash<Vec> {};

Live Demo

Live On Coliru

#include <boost/functional/hash.hpp>
#include <boost/unordered_set.hpp>
#include <iostream>
#include <unordered_set>

class Vec {
private:
std::vector<int> v;

friend size_t hash_value(const Vec& v) {
return boost::hash_value(v.v);
//return boost::hash_range(begin(v.v), end(v.v));
}
friend bool operator==(const Vec& lhs, const Vec& rhs) {
return lhs.v == rhs.v;
}
};

template <> struct std::hash<Vec> : boost::hash<Vec> {};

int main() {
Vec v;
std::unordered_set<Vec> s1;
s1.insert(v);
boost::unordered_set<Vec> s2;
s2.insert(v);
}

In which namespace do hashT functors for user types belong?

First of all, there is no "argument dependent lookup" for templates. So hash<Key> will always refer to the same template, either in std or in global namespace, independent of Key. If it had resolved to different templates in different translation units, it would cause undefined behavior by ODR violation. This alone suggests that hash here means std::hash, that is as-if unordered_map was declared like this:

namespace std {
template<class T> struct hash;

template <class Key,
class T,
class Hash = hash<Key>, // resolves to std::hash<Key> for all Keys
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<std::pair<const Key, T> > >
class unordered_map;
}

However, the types declared in the standard headers are not required to be written in source (they could be in principle built-in to the compiler or pre-compiled by some other magic). The standard requires each standard header to declare only the types in its synopsis, which means that by omitting the std::hash declaration the standard permits some hypothetical implementation to avoid the above namespace pollution. This explains why you do not see the above declaration in the synopsis.

To further back-up the above conclusion, we go to §20.8.12 Class template hash [unord.hash] that reads:

The unordered associative containers defined in 23.5 use specializations of the class template hash as the default hash function.

This paragraph refers to the std::hash, which we can infer from the synopsis of <functional>.

Bottom line: This is an inconsistency in the standard formatting. There are plenty of inconsistencies, so this specific case is not surprising at all. In such cases one has to understand what has been intended by deducing what is the only sensible thing.

Specialization. You specialize templates in the namespace that they were declared. You are explicitly granted the right to specialize standard templates for your own type:

namespace std {
template<> struct hash<YourClass> {
// specialization goes here
};
}


Related Topics



Leave a reply



Submit