Reputation: 155
I'm trying to implement custom hash functors for integer types for use with std::unordered_map
. I would like to do this by providing an implementation for unsigned 64-bit integers and delegating all other implementations to that one via casting/widening.
I've successfully done this in the past by just defining specializations for each additional type I wanted:
template <typename T> struct custom_hash { size_t operator()(const T&) const };
template <> struct custom_hash<uint64_t> { size_t operator()(uint64_t x) const { /* ... */ }};
template <> struct custom_hash<int> { custom_hash<uint64_t> h; size_t operator()(int x) const { return h(x); }};
/* ... */
But I'd like to know how to do this without a specialization for every additional type.
I tried something I read on SO using std::enable_if
and std::is_integral
:
template <typename T> struct custom_hash { /* ... */ };
template <> struct custom_hash<uint64_t> { /* ... */ };
template <typename Int, typename = typename enable_if<is_integral<Int>::value, Int>::type>
struct custom_hash<Int> {
custom_hash<uint64_t> h;
size_t operator()(Int x) const {
return h(x);
}
};
But that didn't work. Clang complains with
error: default template argument in a class template partial specialization
and
error: too many template parameters in template redeclaration
I think this is happening because the declaration collides with the earlier declaration with no definition. I don't know enough about templates to fix that.
Upvotes: 0
Views: 494
Reputation: 45674
The problem is that all template-arguments of a template-specialization must be deducible from the base-template, not from each other.
If you can add a dummy-argument to the base-template, or do the specialization in a base under your control, you are golden though:
template <typename T, class = void>
struct custom_hash;
template <>
struct custom_hash<uint64_t>
{ size_t operator()(uint64_t x) const { /* ... */ }};
template <class T>
struct custom_hash<T, std::enable_if_t<std::is_integral<T>() && sizeof(T) <= sizeof(uint64_t)>
{ size_t operator()(int x) const { custom_hash<uint64_t> h; return h(uint64_t(x)); }};
Upvotes: 3