3

Let’s say we have the following class:

template <int i>
class S {
public:
    enum class E : signed char {a, b, c};
    // ...
};

And now I want to have a hash function for E. The following works:

namespace std {
template<>
struct hash<typename S<3>::E> {
    size_t operator()(typename S<3>::E const& e) const { return 0; }
};
}

But if I make it generic it does not:

namespace std {
template<int i>
struct hash<typename S<i>::E> {
    size_t operator()(typename S<i>::E const& e) const { return 0; }
};
}
// error C2764: 'i': template parameter not used or deducible in partial specialization 'std::hash<S<i>::E>'

How can I make sure to always be able to hash S::E for all i?

Petter
  • 37,121
  • 7
  • 47
  • 62
  • 2
    What if all `S` had `E` be a typedef of some other single type? `i` can't be figured out in the general sense. It isn't quite the same, but there are some similar answers on here. Found one: http://stackoverflow.com/questions/6060824/c-template-argument-can-not-be-deduced – chris Feb 20 '16 at 01:47

1 Answers1

4

typename S<3>::E denotes a particular type. It is fine to declare an explicit specialization of std::hash for that type. If that type is ever used as a template argument to std::hash, then your explicit specialization will be used.

On the other hand, there is no way that your partial specialization

template<int i>
struct hash<typename S<i>::E>

can ever be used. As the error says, i is not deducible. For any given T, the compiler cannot determine whether std::hash<T> should use your partial specialization because it has no way of telling whether there exists an i such that typename S<i>::E is the same type as T. If i were used in a deduced context, such as:

template<int i>
struct hash<S<i>>

it would be different, because then the compiler could simply attempt to deduce i. The compiler can know for sure whether a given type is S<i> where S is a template known to it. If T is S<i> for some i, then the deduction succeeds, otherwise it fails and the specialization is ignored. In a non-deduced context, the compiler can't tell.

How to fix? If E were a class type, I would suggest making it a template that would not be nested inside S. Then you could specialize std::hash for E<i>. However, you can't have enum templates, so that's out. You could perhaps just have a single non-template enum and a hash specialization for that type, then have S<i>::E be a typedef to that enum for all i. If you don't want users to rely on the fact that S<i>::E is the same type for all i, you can just say that the definition of S<i>::E is unspecified.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312