定义std::hash<std::function>是什么意思?

6
我需要创建一个模板类,可以保存指向类型为T的元素的指针,并对它们执行函数。这些函数来自不同的地方,因此我需要一个容器来存储它们,以便稍后调用。我决定使用std::unordered_set,因为它提供了速度并限制了重复,因为它实现为哈希表。我已经编写了整个类,但由于没有为我的接受类型为T指针并返回voidstd::function定义哈希函数,所以它无法编译。很容易用struct hash<std::function<void(MyCustomType*)>>(也要重载()运算符)为我使用的每种类型指定它,但是如何实际散列函数呢?
下面是我的类中与相关成员和方法有关的精简摘录:
template <typename T>
class Master {
private:
    std::unordered_set<std::function<void(T*)>> functions;
protected:
    registerFunction(std::function<void(T*)> function) {
        this->functions.insert(function);
    }
    unregisterFunction(std::function<void(T*)> function) {
        this->functions.erase(function);
    }
};

我并不完全依赖于使用std :: unordered_set,但它似乎提供我需要让这段代码(以及我的其他代码)正常工作的一切。
我想错了吗? 完全无法对std :: function进行哈希处理吗?

1
你还需要定义一些 op==,但这几乎是不可能做到的。 - PlasmaHH
1
你面临的问题是 std::function 可以持有任何兼容类型的 任何 函数对象。这就是它的全部意义,即类型擦除。如果没有访问其中包含的内容,你就无法对其进行哈希(或比较值)。即使 std::function 想要给你访问权限,它基本上也做不到:你无法编写一个可以处理它的函数,因为类型可能是任何东西。 - Steve Jessop
2
几年后重新思考这个问题,适当的解决方案是在注册时生成一个ID,并要求调用者在注销时提供该ID。如果确实需要唯一性,那么在注册时也应该要求调用者提供ID,因为函数的唯一性不应该是注册/注销类的关注点。我想我只是过度设计了。特别是因为这段代码已经使用了几年,甚至从未需要注销... - Andrew Larsson
1个回答

3

集合大多用于检查数据是否在其中。

所以我不明白在这里使用它的意义...您将拥有函数,并将它们存储在集合中,之后呢?您只需要对它们进行迭代吗?

对于您的问题,集合元素应该具有生成哈希和operator==()的方法。第二个条件对于std::function并未提供,因此您无法检查您的函数是否真的在集合中。

因此,即使您找到了一种从函数生成哈希的方法,您也会陷入困境...我不知道如何满足哈希要求。

为什么不直接使用std::vector


我考虑过转换到 std::vector,在听到这个之后,我现在可能会这样做。我只是得不到我所希望的独特性。我只需要在 registerFunction() 方法中返回索引以供在 unregisterFunction() 中使用即可。谢谢! - Andrew Larsson
返回索引将无法正确工作,因为在向量中删除元素时可能会使它们失效。您可以切换到std::list并返回迭代器或在向量中存储虚拟函数以在删除元素时使用。 - Nevin
@AndrewLarsson 即使有注册机制,我也不知道你如何防止重复调用。如果你真的想要某种唯一性控制,为什么不强制使用 std::unordered_map<std::string, std::function<XXX>> 来命名函数?你代码的用户将不得不使用两个不同的名称注册其函数,并且这将涉及到双重调用点。你甚至可以使用一些臭名昭著的宏来自动关联名称和函数。 - Johan
@Johan 我想现在我只能相信用户只注册他们的函数一次,因为它似乎比目前的情况更值得付出的努力,但是 std::unordered_map 的想法会非常好! - Andrew Larsson
我最终使用了 std::list<std::function<void(std::shared_ptr<T>)>> functions; 并取消了注销函数的能力。 - Andrew Larsson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接