使用对象的基类的部分模板特性对std :: hash哈希对象

时间:2018-10-16 07:56:24

标签: c++ templates

我有一个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。叮当声:

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'

我找到了this解决方案,但我想避免使用宏。而且,这与我对继承的期望相反。

有解决方案吗?子类可以继承其基类对std::hash的专长吗?

此外,我不确定100%使用std::enable_ifstd::is_base_of。您能否告诉我,假设可以推论T,这是否可行?

1 个答案:

答案 0 :(得分:1)

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;