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:
- Overload
boost::hash_value
for your own type. - Specialize
std::hash
for your own type and implement it usingboost::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
Return Statement VS Exit() in Main()
Why Can't I Initialize Non-Const Static Member or Static Array in Class
Is There a Replacement For Unistd.H For Windows (Visual C)
Detect If Stdin Is a Terminal or Pipe
How to Use the Qt'S Pimpl Idiom
How to Make a .Lib File When Have a .Dll File and a Header File
Why Can't Variable Names Start With Numbers
Create Random Number Sequence With No Repeats
Selectively Disable Gcc Warnings For Only Part of a Translation Unit
When Can Outer Braces Be Omitted in an Initializer List
Why Do Constant Expressions Have an Exclusion For Undefined Behavior
Type of Integer Literals Not Int by Default
No Matching Function - Ifstream Open()
Writing Your Own Stl Container
Why Are Unnamed Namespaces Used and What Are Their Benefits
What's "Wrong" With C++ Wchar_T and Wstrings? What Are Some Alternatives to Wide Characters