Reputation: 7304
I'm converting a C++ program over from Windows building with Visual C++ 2008 to build on Linux with gcc 4.6.1. There's a module that uses <unordered_map>
. In VC++, it seems to be perfectly ok to
#include <unordered_map>
...
std::tr1::unordered_map<mystruct, int> my_map;
We're actually supporting more compilers than just gcc 4.6 and VC++ 2008, so using pure C++2011 code isn't feasible. gcc gets upset with #include <unordered_map>
, complaining that this is the true-blue c++2011 include file, so one of the things I had to do to make this work was change the include to
#include <tr1/unordered_map>
...
std::tr1::unordered_map<mystruct, int> my_map;
This works. Fair enough. Now I've got another problem, though. Here's the definition for mystruct:
struct mystruct
{
#ifdef __cplusplus
inline operator size_t() const
{
return m_val;
}
#endif
unsigned int m_val;
};
In VC++ 2008, this seems to be as much as std::hash
needs to specialize on mystruct
. std::tr1::hash
, on the other hand, doesn't like this, at least not on gcc 4.6.1. It refuses to link, complaining that std::tr1::hash<mystruct>::operator()( mystruct ) const
is undefined. I'm not sure whether that happens with VC++ when I do the proper tr1
include - maybe it'll complain about the same thing? I'll try it tomorrow, but for now all I've got is a linux box with gcc on it. For now, I had to do this to get it working:
namespace std {
namespace tr1 {
std::size_t hash<mystruct>::operator()( mystruct & c ) const
{
return c.m_val;
}
}
}
Can anyone enlighten me on how this is supposed to work? It seems so much more elegant to be able to define a size_t
operator on a type that you want to be hashable, but I'm willing to live with defining the operator()
on std::tr1::hash
.
UPDATE:
I tried specializing the entire hash class, as suggested. Building with gcc, I get
myfile.cpp:41:12: error: specialization of 'std::tr1::hash<mystruct>' after instantiation
myfile.cpp:41:12: error: redefinition of 'struct std::tr1::hash<mystruct>'
/usr/include/c++/4.6/tr1/functional_hash.h:45:12: error: previous definition of 'struct std::tr1::hash<mystruct>'
Upvotes: 1
Views: 4762
Reputation: 137910
Microsoft's way, to accept an implicit conversion to std::size_t
, is an extension.
GCC's way, to specialize std::tr1::hash
, is the way actually defined by TR1 which is now standardized in C++11 (of course with the tr1::
part dropped).
MSVC should still accept the hash
specialization, and of course both should accept a completely new class passed as the Hasher template argument.
It is better style to specialize the entire class, not just the operator()
function:
namespace std {
namespace tr1 {
template<>
struct hash< mystruct > {
std::size_t operator()( mystruct & c ) const
{
return c.m_val;
}
};
}
}
Upvotes: 6
Reputation: 827
Since your mystruct
is defined by user, you need to provide a hash function for unordered_map
:
struct my_hash {
std::size_t operator()( mystruct & c ) const
{
return c.m_val;
}
};
std::tr1::unordered_map<mystruct, int, my_hash> my_map;
Upvotes: 2