dysonsfrog
dysonsfrog

Reputation: 155

C++ template specialization - delegating other integer types to uint64_t

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

Answers (1)

Deduplicator
Deduplicator

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

Related Questions