[英]Hash an object using its base class' partial template specialization for std::hash
I have a wrapper class for std::string
that serves as base class for several others.我有一个
std::string
的包装类,作为其他几个的基类。 Instances of the subclasses will be used as keys in std::unordered_set
so I need to provide a hash function for them.子类的实例将用作
std::unordered_set
键,因此我需要为它们提供散列函数。 Since the hash is only dependent on the std::string
stored in the base class, I do not want to write a hash function for every subclass but rather use the one from the wrapper class.由于散列仅依赖于存储在基类中的
std::string
,我不想为每个子类编写散列函数,而是使用包装类中的散列函数。
This is how I would like to solve the problem:这就是我想解决这个问题的方式:
#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;
}
This does not compile of course, since T
cannot be deduced.这当然不能编译,因为无法推导出
T
Clang says: 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'
And g++ says: 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'
I have found this solution, but I would like to avoid using a macro.我找到了这个解决方案,但我想避免使用宏。 Also, this goes against what I expect from inheritance.
此外,这与我对继承的期望背道而驰。
Is there a solution for this?有解决方案吗? Can a subclass inherit its base class' specialization of
std::hash
?子类可以继承其基类对
std::hash
吗?
Also, I'm not 100% sure about my use of std::enable_if
and std::is_base_of
.另外,我不是 100% 确定我使用
std::enable_if
和std::is_base_of
。 Could you tell me whether this would work assuming T
could be deduced?如果可以推导出
T
你能告诉我这是否可行?
IRC, the problem with std::enable_if
is that it does not work for classes with a single template parameter. IRC,
std::enable_if
的问题在于它不适用于具有单个模板参数的类。 Consequently, you cannot specialize std::hash
by using std::enable_if
.因此,您不能使用
std::enable_if
专门化std::hash
。
However, you can make your own hasher as follows:但是,您可以按如下方式制作自己的哈希器:
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(); }
};
And then use it as a second template argument of std::unordered_set
:然后将其用作
std::unordered_set
的第二个模板参数:
std::unordered_set<Wrapper, WrapperHasher<Wrapper>> m1;
std::unordered_set<Derived, WrapperHasher<Derived>> m2;
But in your case, you can define a wrapper much more simply as:但在你的情况下,你可以更简单地定义一个包装器:
struct WrapperHasher {
std::size_t operator()(const Wrapper& k) const { return k.hash(); }
};
And then write:然后写:
std::unordered_set<Wrapper, WrapperHasher> m1;
std::unordered_set<Derived, WrapperHasher> m2;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.