Хешируйте объект, используя частичную специализацию шаблона его базового класса для std :: hash - PullRequest
0 голосов
/ 16 октября 2018

У меня есть класс-оболочка для std::string, который служит базовым классом для нескольких других.Экземпляры подклассов будут использоваться в качестве ключей в std::unordered_set, поэтому мне нужно предоставить для них хеш-функцию.Поскольку хеш зависит только от std::string, хранящегося в базовом классе, я не хочу писать хеш-функцию для каждого подкласса, а вместо этого использую функцию из класса-оболочки.

Вот как я хотел бы решить эту проблему:

#include <string>
#include <unordered_set>

class Wrapper {
public:
  std::string name;
  size_t _hash;
  explicit Wrapper(std::string str) : name(str), _hash(std::hash<std::string>()(name)) {}
  size_t hash() const { return _hash; }
};

class Derived : public Wrapper {};

namespace std {

template <> struct hash<Wrapper> {
  std::size_t operator()(const Wrapper &k) const { return k.hash(); }
};

template <typename T> struct hash<std::enable_if_t<std::is_base_of_v<Wrapper, T>>> {
  std::size_t operator()(const T &k) const { return k.hash(); }
};

} // namespace std

int main(void) {
  std::unordered_set<Wrapper> m1;
  std::unordered_set<Derived> m2;
}

Конечно, это не компилируется, так как T не может быть выведено.Clang говорит:

20:30: error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
20:20: note: non-deducible template parameter 'T'

А g ++ говорит:

hash_subclass.cpp:21:30: error: template parameters not deducible in partial specialization:
 template <typename T> struct hash<std::enable_if_t<std::is_base_of_v<Wrapper, T>>> {
                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
hash_subclass.cpp:21:30: note:         'T'

Я нашел это решение, но я бы хотел избежать использования макроса.Кроме того, это противоречит тому, что я ожидаю от наследования.

Есть ли решение для этого?Может ли подкласс наследовать специализацию своего базового класса std::hash?

Кроме того, я не уверен на 100% в своем использовании std::enable_if и std::is_base_of.Не могли бы вы сказать мне, будет ли это работать, если предположить, что T может быть выведено?

1 Ответ

0 голосов
/ 16 октября 2018

IRC, проблема с std::enable_if заключается в том, что он не работает для классов с одним параметром шаблона.Следовательно, вы не можете специализировать std::hash, используя std::enable_if.

. Однако вы можете создать свой собственный хеш-код следующим образом:

template <typename T, typename Enable = std::enable_if_t<std::is_base_of_v<Wrapper, T>>>
struct WrapperHasher {
   std::size_t operator()(const T& k) const { return k.hash(); }
};

И затем использовать его в качестве второго аргумента шаблонаstd::unordered_set:

std::unordered_set<Wrapper, WrapperHasher<Wrapper>> m1;
std::unordered_set<Derived, WrapperHasher<Derived>> m2;

Но в вашем случае вы можете определить оболочку гораздо проще:

struct WrapperHasher {
   std::size_t operator()(const Wrapper& k) const { return k.hash(); }
};

А затем напишите:

std::unordered_set<Wrapper, WrapperHasher> m1;
std::unordered_set<Derived, WrapperHasher> m2;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...