How to Specialize Std::Hash≪Key≫::Operator() For User-Defined Type in Unordered Containers

How to specialize std::hash<Key>::operator() for user-defined type in unordered containers?

You are expressly allowed and encouraged to add specializations to namespace std*. The correct (and basically only) way to add a hash function is this:

namespace std {
template <> struct hash<Foo>
{
size_t operator()(const Foo & x) const
{
/* your code here, e.g. "return hash<int>()(x.value);" */
}
};
}

(Other popular specializations that you might consider supporting are std::less, std::equal_to and std::swap.)

*) as long as one of the involved types is user-defined, I suppose.

How to specialize std::hash<T> for user defined types?

One easy way is to use boost::hash library and extend it for your type. It has a nice extension function hash_combine (std::hash lacks that) that allows easy composition of hashes of individual data members of your structures.

In other words:

  1. Overload boost::hash_value for your own type.
  2. Specialize std::hash for your own type and implement it using boost::hash_value.

This way you get the best of std and boost worlds, both std::hash<> and boost::hash<> work for your type.


A better way is to use the proposed new hashing infrastructure in N3980 Types Don't Know #. This infrastructure makes hash_combine unnecessary.

Cannot specialize std::hash to store custom type in unordered_map

If I move the templahttps://stackoverflow.com/help/how-to-answerte specialization into the header file where the Tile class is defined (after the definition of the class of course, but ANYWHERE after that), the code compiles.

Normally, when you also implement the class yourself, this shouldn't matter. I think it has something to do with this class being implemented in a shared library. I'm no C++ guru, so I cannot explain this. But I'd love to here someone still explain it.

where should I put the specialized std::hash for user defined type

Thanks to everyone.

I have found the reason and solved the problem somehow: visual studio accepted the InstanceHash when I was defining instances_. Since I was changing the use of set to unordered_set, I forgot to specify InstanceHash when I tried to get the const_iterator, so this time the compiler tried to use the std::hash<> things and failed. But the compiler didn't locate the line using const_iterator, so I mistakenly thought it didn't accept InstanceHash when I was defining instances_.

I also tried to specialize the std::hash<> for class Instance. However, this specialization requires at least the declaration of class ca::Instance and some of its member functions to calculate the hash value. After this specialization, the definition of class ca::InstanceManage will use it.

I now generally put declarations and implementations of almost every classes and member functions together. So, the thing I need to do is probably to split the ca namespace scope to 2 parts and put the std{ template <> struct hash<ca::Instance>{...}} in the middle.

Are there no specializations of std::hash for standard containers?

I'm not sure why the standard library hasn't included this, but Boost has hashing for all sorts of things made up from hashable types. The key function for this is hash_combine, which you are welcome to copy from boost/functional/hash/hash.hpp.

Using hash_combine, Boost derives a range_hash (just combining the hashes of a each element of a range), as well as pair and tuple hashers. The range_hash in turn can be used to hash any iterable container.

How to specialize std::hash for type from other library

You can just specialise std::hash for your type:

namespace std {
template <>
struct hash<FullyQualified::LibEnum> {
size_t operator ()(FullyQualified::LibEnum value) const {
return static_cast<size_t>(value);
}
};
}

Alternatively, you can define a hash type where ever you like and just provide it as the additional template argument when instantiating std::unordered_map<FooEnum>:

// Anywhere in your code prior to usage:

struct myhash {
std::size_t operator ()(LibEnum value) const {
return static_cast<std::size_t>(value);
}
};

// Usage:

std::unordered_map<LibEnum, T, myhash> some_hash;

Neither methods require you to modify the library.

Unable to find a user-defined type in a c++ unordered set with custom operator==()

You are trying to compare pointers, not values.
You need to specify hashing function for class Block.

For example, if you want to use mName as key the code will be the following:

class Block {
private:
string mName;
string mLib;
public:
Block(string const& name, string const& lib)
{
mName = name;
mLib = lib;
}
string getName() const {
return mName;
};
string getLib() const {
return mLib;
}
bool operator==(const Block & block) const;
};

template<> struct std::hash<Block> {
std::size_t operator()(const Block & block) const noexcept {
return std::hash<std::string>{}(block.getName());
}
};

bool Block::operator==(const Block & block) const {
return block.getName() == mName;
}

int main() {
std::vector<Block> vertices;

vertices.emplace_back(Block("mod1", "work"));
vertices.emplace_back(Block("mod2", "work"));
vertices.emplace_back(Block("mod3", "work"));

std::unordered_set<Block> undefs;
undefs.emplace(Block("mod1", "work"));
undefs.emplace(Block("mod2", "work"));

for (auto& vertex : vertices) {
auto search = undefs.find(vertex);
if (search != undefs.end()) {
std::cout << "Block: " << vertex.getName() << "\n";
}
}
}

Forward declaration of std::hash<MyClass>

You have to define the std::hash<A> specialization in A.h, so that B and C will be able to see that the specialization exists and that it has an operator() member.

If B and C can't "see" the std::hash<A> specialization, then they will instantiate the primary std::hash template, which is disabled because the standard library doesn't know how to hash your user-defined type A.

If you declare the std::hash<A> specialization in A.h, but you do not define it, then B and C will see it as an incomplete class, which means the operator() cannot be called.

So you must define std::hash<A> in A.h. You can do so like this:

// A.h
namespace std {
template <> struct hash<A> {
std::size_t operator()(const A&) const noexcept { ... }
};
}

You could also move the definition of operator() to the A.cpp file:

// A.h
namespace std {
template <> struct hash<A> {
std::size_t operator()(const A&) const noexcept;
};
}

// A.cpp
namespace std {
std::size_t hash<A>::operator()(const A&) const noexcept { ... }
}


Related Topics



Leave a reply



Submit