我有一个用于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'
我已经找到了这个解决方案,但我想避免使用宏。此外,这与我对继承的期望背道而驰。
有解决方案吗?子类可以继承其基类的std::hash
特化吗?
另外,我不是100%确定我使用std::enable_if
和std::is_base_of
。你能告诉我假设可以推断出T
这是否可行吗?
IRC,std::enable_if
的问题在于它不适用于具有单个模板参数的类。因此,您不能通过使用std::enable_if
来专门化std::hash
。
但是,您可以按如下方式创建自己的哈希器:
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;