【发布时间】:2017-08-22 16:08:22
【问题描述】:
我有一个使用Howard Hinnant's method 实现的哈希过程(基于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));
}
所以我需要为A 重载hash_append:
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 函数的计算量很大,所以我想使用Inputs 的哈希值将先前计算的结果保存在文件中,例如类似:
// 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的正确版本,但我没有不要认为这正是您要寻找的。你考虑过吗?缺点是您必须向这些类添加样板才能接受访问者。如果它对您有用,我可以将评论放在更详细的答案中。 -
@skypjack 对不起,我不完全理解你的意思——你能写一个小例子来说明你的意思吗?
-
我的意思是along this line。这不是一个可行的示例,但您应该明白这一点。不幸的是,您的示例代码缺少很多代码来打包一个具体的示例,我很抱歉。当然,
HashVisitor可以通过使用可变参数模板和直接继承来简化(至少,这样您就不必每次定义新类型时都修改它),但是我定义的方式应该更容易理解。 -
@skypjack 感谢您的示例。这是一个有趣的想法,至少它消除了我第一个想法的第二个缺点。你写它的方式,它看起来很健壮(如果我添加一个新类
D,除非我向访问者添加D,否则它不会编译),但是是否可以简化HashVisitor而不会丢失它鲁棒性?因为如果我去掉这个健壮性,这看起来类似于dynamic_cast版本。 -
如果您发现它是一个可行的解决方案,让我稍微修改一下并将其放入答案中。
标签: c++ hash polymorphism c++14