Suppose I got a core library in which I'm trying to add explicit specialization of std::hash
for my core classes, including class Foo
(and many others I'd like to also specialize std::hash
with).
Foo.h
struct Foo
{
...
size_t _id;
};
KeyHashing.h, lives in same library as Foo
#include "Foo.h"
#include <unordered_map>
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(const Foo& t) const noexcept; // Link error, unresolved external when used in client code
};
// extern template struct hash<Foo>; // error C2039 '()': is not a member of 'std::hash<Foo>'
}
Concerned about adding templates definitions in my header which are gonna get compiled in the translation units that they get included into and possibly increasing my total exe size and compile time, I'd like the implementation for operator()
to live in KeyHashing.cpp (in the core library) and to look something like this.
KeyHashing.cpp
#include "Foo.h"
// https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x
template <typename T, typename... Rest>
void HashCombine(size_t& seed, const T& v, Rest... rest)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
(HashCombine(seed, rest), ...);
}
namespace std {
size_t hash<Foo>::operator()(const Foo& t) const noexcept // error C2039 when used with extern template struct hash<Foo> in header.
{
std::size_t ret = 0;
HashCombine(ret, t._id);
return ret;
}
}
And in my client code (outside of core lib), I'd use std::unordered_map
with my internal core classes as keys seamlessly.
So 2 questions:
UPDATE
The code above doesn't link, with error
11>MyClass.obj : error LNK2019: unresolved external symbol "public: unsigned __int64 __cdecl std::hash<struct Foo>::operator()(struct Foo const &)const " (??R?$hash@UFoo@@@std@@QEBA_KAEBUFoo@@@Z) referenced in function "public: unsigned __int64 __cdecl std::_Uhash_compare<struct Foo,struct std::hash<struct Foo>,struct std::equal_to<struct Foo> >::operator()<struct Foo>(struct Foo const &)const " (??$?RUFoo@@@?$_Uhash_compare@UFoo@@U?$hash@UFoo@@@std@@U?$equal_to@UFoo@@@3@@std@@QEBA_KAEBUFoo@@@Z)
and as mentioned in comments the exe size may or may not be affected due to ODR, but the compile time is as relevant as any other metric so is there any syntax I'm missing here?
Ok so it turns out that my particular issue was the lack __declspec(dllexport)
for the operator() that I defined in my core lib. So although j6t's answer can work, it turns out that I am able to have a specialization implementation out-of-line.