vendredi 20 août 2021

How to write a recursive std::hash specialization?

I have defined a class that needs to go into unordered_set and unordered_map so it needs a hash function. As per How to specialize std::hash for type from other library I'm trying to do this with a std::hash specialization. The problem I'm running into is that my class, val, is a recursive type that can contain other vals and needs to calculate a hash from the hashes of the component values.

I've got this far:

inline void hashCombine(size_t &h, size_t x) {
  h ^= x + 0x9e3779b9 + (h << 6) + (h >> 2);
}

namespace std {
template <> struct hash<val> {
  size_t operator()(const val &a) const {
    if (!a.boxed())
      return a.n;
    size_t h = 0;
    for (auto i : a)
      hashCombine(h, hash<size_t>(a[i]));
    return h;
  }
};

(where a[i] is a val)

And get this error message:

./val.h(179,22): error: no matching conversion for functional-style cast from 'val' to 'hash<size_t>' (aka 'hash<unsigned long long>')
      hashCombine(h, hash<size_t>(a[i]));
                     ^~~~~~~~~~~~~~~~~
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include\type_traits(2178,8): note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'val' to
      'const std::hash<unsigned long long>' for 1st argument
struct hash
       ^
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include\type_traits(2178,8): note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'val' to
      'std::hash<unsigned long long>' for 1st argument
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include\type_traits(2178,8): note: candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was
      provided

I have also seen example usages that add an extra () to the line in question - it seems an odd thing for there to be disagreement about, but okay - which would make it

      hashCombine(h, hash<size_t>()(a[i]));

When I try that, I get a different error message:

./val.h(179,23): error: no matching function for call to object of type 'hash<size_t>' (aka 'hash<unsigned long long>')
              hashCombine(h, hash<size_t>()(a[i]));
                             ^~~~~~~~~~~~~~
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include\type_traits(2161,23): note: candidate function not viable: no known conversion from 'val' to 'const unsigned long long' for 1st argument
    _NODISCARD size_t operator()(const _Kty& _Keyval) const

This is one of those times when I'm not sure whether I'm making a trivial syntax error, trying to do something that for fundamental reasons cannot be done, or anywhere in between.

What am I missing?

Aucun commentaire:

Enregistrer un commentaire