How do i declare a hash function for my custom type so that i could use it in an unordered_map?
namespace std {
template<>
struct hash<my_custom_type>
{
using argument_type = my_custom_type;
using result_type = size_t;
size_t operator()(my_custom_type const& x) const
{
// Perform your hash algorithm here.
}
};
}
Probably the best way is to provide a specialization of std::hash< class Key > with std::hash< mytype >.
Related
Say I define a map with a custom comparator such as
struct Obj
{
int id;
std::string data;
std::vector<std::string> moreData;
};
struct Comparator
{
using is_transparent = std::true_type;
bool operator()(Obj const& obj1, Obj const& obj2) { return obj1.id < obj2.id; };
}
std::map<Obj,int,Comparator> compMap;
is there a good way to ensure that downstream users don't have to implement the comparator to use the map as a map?
for instance my compiler throws an error if I try to pass it to a function with a similar type.
template<class T>
inline void add(std::map<T, int>& theMap, T const & keyObj)
{
auto IT = theMap.find(keyObj);
if (IT != theMap.end())
IT->second++;
else
theMap[keyObj] = 1;
}
add(compMap,newObj); //type error here
EDIT:
I kinda over santitized this to make a generic case. and then overlooked the obvious
template<class T, class Comp, class Alloc>
inline void add(std::map<T, int, Comp, Alloc>& theMap, T const & keyObj)
still having issues with one use not being able to deduce T, but went from 80 erros to 1 so... progress
thanks everyone.
You can typedef the specialised type and use that type inplace of
std::map<...
typedef std::map<Obj,int,Comparator> compMap_t;
inline void add(compMap_t& theMap, Obj const & keyObj)
...
Downstream users either use the type declared by you
using my_important_map = std::map<Obj,int,Comparator>;
or better use functions which take a generic map type,
auto some_function(auto const& map_)
{
//do something with the map and don't care about the ordering
return map_.find(Obj(1));
}
I want to declare :
std::unordered_map<CString, CString> m_mapMyMap;
But when I build I got an error telling me that the standard C++ doesn't provide a hash function for CString, while CString have the (LPCSTR) operator.
How do I properly implement a hash function for CString?
Based on the MS STL implementation for std::string I created the following methods which can be used for std::unordered_set and std::unordered_map:
namespace std {
template <>
struct hash<CString>
{ // hash functor for CString
size_t operator()(const CString& _Keyval) const
{ // hash _Keyval to size_t value by pseudorandomizing transform
return (_Hash_seq((const unsigned char*)(LPCWSTR)_Keyval, _Keyval.GetLength() * sizeof(wchar_t)));
}
};
template <>
struct hash<CStringA>
{ // hash functor for CStringA
size_t operator()(const CStringA& _Keyval) const
{ // hash _Keyval to size_t value by pseudorandomizing transform
return (_Hash_seq((const unsigned char*)(LPCSTR)_Keyval, _Keyval.GetLength() * sizeof(char)));
}
};
}
Or even more generic:
namespace std {
template<typename BaseType, class StringTraits>
struct hash<CStringT<BaseType, StringTraits>> : public unary_function<CStringT<BaseType, StringTraits>, size_t>
{ // hash functor for CStringT<BaseType, StringTraits>
typedef CStringT<BaseType, StringTraits> _Kty;
size_t operator()(const _Kty& _Keyval) const
{ // hash _Keyval to size_t value by pseudorandomizing transform
return (_Hash_seq((const unsigned char*)(StringTraits::PCXSTR)_Keyval,
_Keyval.GetLength() * sizeof(BaseType)));
}
};
}
std::unordered_map use std::hash<> that does not use (LPCSTR) operator.
You need to redefine hash function:
template<class T> class MyHash;
template<>
class MyHash<CString> {
public:
size_t operator()(const CString &s) const
{
return std::hash<std::string>()( (LPCSTR)s );
}
};
std::unordered_map<CString,CString,MyHash> m_mapMyMap;
But for better performance use std::string instead CString for key.
After trying out MrTux's suggestion, I have to say that this doesn't work anymore. std::_HashSeq was removed and std::unary_function was also removed in C++17.
I ended up with another solution that incorporates Microsoft's advice for hash implementations to use std::basic_string_view's hasher:
namespace std
{
template<typename BaseType, class StringTraits>
struct hash<ATL::CStringT<BaseType, StringTraits>>
{
size_t operator()(const ATL::CStringT<BaseType, StringTraits>& key) const noexcept
{
return hash<basic_string_view<BaseType>>()(
basic_string_view<BaseType>(
key.GetString(),
key.GetLength()));
}
};
}
I'd like to use a structure just like std::map but without ordering, I don't need ordering and my key is pretty huge, so it's "less than" comparision takes time.
So, I saw unordered_map but it has a hash template argument, so, how to use unordered_map without hashing? I'll really need to build my own container?
This question applies to std::set too.
EDIT
Some answers have suggested to create my own hash, but I can't do this, I should have specified it here. The key contains floating point data, so hashing it would be a real bad idea. I need to compare (std::equal_to) directly.
Create your own hash, it's easily done by composing the overloads of std::hash on the fields of your key.
The cppreference example (same as previous link) is quite good (even if you do not need the template stuff):
struct S
{
std::string first_name;
std::string last_name;
};
template <class T>
class MyHash;
template<>
class MyHash<S>
{
public:
std::size_t operator()(S const& s) const
{
std::size_t h1 = std::hash<std::string>()(s.first_name);
std::size_t h2 = std::hash<std::string>()(s.last_name);
return h1 ^ (h2 << 1);
}
};
After that you can use it in the std::unorderd_map:
std::unordered_map<S, Value, MyHash<S>> the_map;
By the way std::unordered_set also need a hash.
You need to spetialize hash object for your key before declaring your unordered_map.
namespace std
{
template <>
class hash<Key>
{
public:
size_t operator()(const Key &) const
{
// ... your hash function for Key object ...
}
};
}
std::unordered_map<Key, Value> myMap;
Example, if I want you use as a key pair:
namespace std
{
class hash<pair<string, int>>
{
public:
size_t operator()(const pair<string, int> &s) const
{
size_t h1 = hash<string>()(s.first);
size_t h2 = hash<int>()(s.second);
return h1 ^ (h2 << 1);
}
};
}
unordered_map<pair<string, int>, string> myMap;
I wonder if it is possible to use lambda function as custom hash function for unordered_map in C++11? If so, what is the syntax?
#include<unordered_map>
#include<string>
int main() {
auto my_hash = [](std::string const& foo) {
return std::hash<std::string>()(foo);
};
std::unordered_map<std::string, int, decltype(my_hash)> my_map(10, my_hash);
}
You need to pass lambda object to unordered_map constructor, since lambda types are not default constructible.
As #mmocny suggested in comment, it's also possible to define make function to enable type deduction if you really want to get rid of decltype:
#include<unordered_map>
#include<string>
template<
class Key,
class T,
class Hash = std::hash<Key>
// skipped EqualTo and Allocator for simplicity
>
std::unordered_map<Key, T, Hash> make_unordered_map(
typename std::unordered_map<Key, T, Hash>::size_type bucket_count = 10,
const Hash& hash = Hash()) {
return std::unordered_map<Key, T, Hash>(bucket_count, hash);
}
int main() {
auto my_map = make_unordered_map<std::string, int>(10,
[](std::string const& foo) {
return std::hash<std::string>()(foo);
});
}
I am trying to do the following:
boost::unordered_map<boost::flyweight<std::string>, boost::flyweight<std::string> > map;
boost::flyweight<std::string> foo(name);
map[foo] = foo;
But the compiler complains:
"error C2665: 'boost::hash_value' : none of the 17 overloads could convert all the argument types".
But I have defined the following function:
std::size_t hash_value(const boost::flyweight<std::string> & b)
{
boost::hash<std::string> hasher;
const std::string & str = b.get();
return hasher(str);
}
bool operator==(const boost::flyweight<std::string>& f, const boost::flyweight<std::string> & second)
{
return f.get() == second.get();
}
But it doesn´t compile.
What do I need to do to make boost unordered_map to support flyweight?
[EDIT]
I got it to work with the following code:
struct flyweight_hash
{
std::size_t operator()(const boost::flyweight<std::string> &elm) const
{
boost::hash<std::string> hasher;
const std::string & str = elm.get();
return hasher(str);
}
};
and passed it as a template parameter to the construction of the map:
boost::unordered_map<boost::flyweight<std::string>, boost::flyweight<std::string> , flyweight_hash > map;
In this case I don´t understand way overloading hash_value didn´t worked.
boost::hash calls hash_value through argument dependent lookup (ADL). You are trying to define a hash_value function for a class in namespace boost. Hence your hash_value function would need to go into this namespace as well for ADL to work. Unfortunately, adding functions to a foreign namespace is rather evil and should be avoided. Your solution of using a custom hasher seems fine.
A little example code to illustrate:
namespace boost {
// somewhere in boost
template<typename T>
std::size_t hash(const T& t) {
// call using ADL
// e.g. if called with object of class type foo::bar this will
// pick up foo::hash_value despite the lack of namespace
// qualification
return hash_value(t);
}
}
// your hash_value (presumably in the global namespace)
// not picked up by above call
std::size_t hash_value(boost::flyweight<T>...);
namespace boost {
// this would be picked up but is slightly evil
std::size_t hash_value(boost::flyweight<T>...);
}
It is a pity to hash something which has already been hashed. Flyweight keeps a single instance of equal objects, so it is more efficient to hash the address of this instance, instead of its content. I do as follows (in std, not in boost, as I'm using C++11, so I'm extending std::hash, not boost::hash):
namespace std
{
template <typename T>
struct hash<boost::flyweight<T, boost::flyweights::no_tracking>>
{
using value_type = boost::flyweight<T, boost::flyweights::no_tracking>;
size_t operator()(const value_type& ss) const
{
hash<const void*> hasher;
return hasher(&ss.get());
}
};
}
I have been confirmed that this works by design, not by accident: http://lists.boost.org/boost-users/2013/03/78007.php