哈希多态类型的正确方式
Hashing polymorphic type the proper way
我有一个使用Howard Hinnant方法实现的哈希过程(基于hash_append
重载的通用哈希(。
该方法的目的是创建类的哈希以"记住"计算结果(请参阅本答案的末尾(,因此我面临一些问题。具体而言,请考虑以下可能需要哈希处理的可能Input
类:
struct A {
virtual int do_stuff() const = 0;
virtual ~A();
};
struct B: A {
int do_stuff() const override { return 0; }
};
struct C: A {
const int u;
int do_stuff() const override { return u; }
};
struct Input {
A const& a; // store a reference to an instance of B or C
};
现在,如果我想散列Input
,我将得到类似的东西:
template <class HashAlgorithm>
void hash_append(HashAlgorithm& h, Input const& input) {
hash_append(h, typeid(input));
hash_append(h, typeid(input.a));
}
所以我需要超载hash_append
A
:
template <class HashAlgorithm>
void hash_append(HashAlgorithm& h, A const& a) {
hash_append(h, typeid(a));
}
这里的问题是,根据a
的运行时类型,我需要向哈希添加额外的信息,例如,对于C
我需要添加u
。
我想到了以下解决方案(和缺点(:
向
A
添加一个虚拟方法,该方法返回可添加到typeid()
哈希的特定值,但是:- 这意味着在
A
中添加一个与A
目的无关的方法,因此我不太喜欢这个想法(特别是因为我有多个类似A
类的类(; - 这打破了
hash_append
的概念,因为该方法将具有所有继承类的唯一返回类型。
- 这意味着在
在
hash_append
里面做一堆dynamic_cast
:- 我觉得这很丑...特别是如果我有多个类似于
A
的类; - 这很容易出错:如果有人添加了
A
的新子项并且没有在hash_append
中添加dynamic_cast。
- 我觉得这很丑...特别是如果我有多个类似于
有没有办法散列多态类型,而不必修改类型本身或依赖一堆dynamic_cast
?
这样做的最终目标是能够记住一些重函数的结果。让我们勾勒出我的应用程序的基本结构:
struct Input { };
struct Result { };
Result solve(Input const&);
solve
函数的计算量很大,所以我想使用Input
s 的哈希将先前计算的结果保存在文件中,例如:
// depends on hash_append
std::string hash(Input const&);
Result load_or_solve(Input const& input) {
auto h = hash(input);
Result result;
if (exists(h)) { // if result exists, load it
result = load(h);
}
else { // otherwize, solve + store
result = solve(input);
store(h, result);
}
return result;
}
load
和store
方法将从文件中加载和存储结果,目标是记住不同运行之间的解决方案。
如果您对如何在不必处理上述问题的情况下记住这些结果有建议,我将很高兴阅读它们。
您可以在A
的hash_append
版本中使用双重调度,并将请求转发到正确的版本(即用于B
或C
的版本(。缺点是您必须向这些类添加样板才能接受访问者,我不能说您是否可以接受。
这里有一堆代码应该说明这个想法:
struct B;
struct C;
struct Visitor {
virtual void visit(const B &) = 0;
virtual void visit(const C &) = 0;
};
template<typename T, typename... O>
struct HashVisitor: T, HashVisitor<O...> {
template<typename U>
std::enable_if_t<std::is_same<T, U>::value> tryVisit(const U &u) {
T::operator()(u);
}
template<typename U>
std::enable_if_t<not std::is_same<T, U>::value> tryVisit(const U &u) {
HashVisitor<O...>::visit(u);
}
void visit(const B &b) override { tryVisit<B>(b); }
void visit(const C &c) override { tryVisit<C>(c); }
};
template<>
struct HashVisitor<>: Visitor {};
template<typename... F
auto factory(F&&... f) {
return HashVisitor<std::decay_t<F>>{std::forward<F>(f)...};
}
struct A {
virtual void accept(Visitor &) = 0;
virtual int do_stuff() const = 0;
virtual ~A();
};
struct B: A {
void accept(Visitor &v) override { v.visit(*this); }
int do_stuff() const override { return 0; }
};
struct C: A {
const int u;
void accept(Visitor &v) override { v.visit(*this); }
int do_stuff() const override { return u; }
};
template <class HashAlgorithm>
void hash_append(HashAlgorithm &, const B &) {
// do something
}
template <class HashAlgorithm>
void hash_append(HashAlgorithm &, const C &) {
// do something
}
template <class HashAlgorithm>
void hash_append(HashAlgorithm &h, const A &a) {
auto vis = factory(
[&h](const B &b){ hash_append(h, b); },
[&h](const C &c){ hash_append(h, c); }
);
a.accept(vis);
}
相关文章:
- 多态性和功能结合
- 具有默认模板参数的多态类的模板推导失败
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 多态二进制函数
- 访问存储在向量C++中的结构的多态成员
- 使用取消引用的指针的多态性会产生意外的结果.为什么?
- 哈希多态类型的正确方式
- 这是多态性(C++/程序化编程方式)吗?
- 以多态方式删除对象
- 以多态的方式处理非多态对象,不会产生性能开销
- 允许运行时和编译时多态性的灵活方式
- 如何在C++中创建一个工厂方法,允许以多态方式返回不同的类型
- 我如何使连续内存以一种安全的方式表现多态
- 如何以多态方式无限嵌套对象
- 对于普通数据对象,多态的正确方式是什么?
- 函数,该函数以多态方式处理vector和hash_set
- 以多态方式使用派生类的std::vector成员的复制赋值会导致内存泄漏
- 以非多态方式调用虚拟函数的成本是多少
- 根据"foreign"子类型采取特定操作,不带开关、强制转换等。我们可以以某种方式使用多态性吗?
- 以惯用的方式实现多态运算符==(C++)