我的班级有一个 toString() 方法,我如何使用它在 std::unordered_set 中进行散列?

Posted

技术标签:

【中文标题】我的班级有一个 toString() 方法,我如何使用它在 std::unordered_set 中进行散列?【英文标题】:My class has a toString() method, how do I use this for hashing in std::unordered_set? 【发布时间】:2014-10-09 15:54:14 【问题描述】:

MyClass 定义了operator== 并具有非平凡的内部状态,但它确实提供了一个wstring toString() 方法,该方法返回该状态的序列化版本。所以我认为在std::unordered_set 上使用toString()hash<wstring> 会很容易。

但是是否可以在不定义无关函子类的情况下以一种很好的方式做到这一点?在迁移到 VS2013 之后,我才刚刚开始掌握 C++11,我认为这是向前迈出的一大步,能够定义诸如 lambdas 之类的东西?

感谢任何建议如何最好地做到这一点。

【问题讨论】:

【参考方案1】:
auto hasher = [](const MyClass &m) return std::hash<std::wstring>()(m.toString()); ;
std::unordered_set<MyClass, decltype(hasher)> set(10, hasher);

很遗憾,目前这不适用于 MSVC due to a bug。

可能的变通方法包括为MyClass 编写std::hash 的特化,或将lambda 存储在std::function&lt;std::size_t(const MyClass &amp;)&gt; 中并将其用作哈希器的类型:

std::function<std::size_t(const MyClass &)> hasher = 
                [](const MyClass &m)  return std::hash<std::wstring>()(m.toString()); ;
std::unordered_set<MyClass, std::function<std::size_t(const MyClass &)>> set(10, hasher);

【讨论】:

【参考方案2】:

最好的方法是告诉std::hash 如何通过专门化来散列MyClass

namespace std 
template <>
struct hash<MyClass> 
  std::size_t operator () (const MyClass& mc) const 
    return std::hash<std::wstring>()(mc.toString());
  
;
 // namespace std

因此您无需为unordered_setunordered_map 的非默认模板参数操心。

【讨论】:

奇怪的是,std::hash 的专业化似乎需要提供result_typeargument_type 类型定义,无论出于何种原因。此外,问题要求 lambdas。 @T.C.该问题要求“建议如何最好地做到这一点”。最好的方法是毫无疑问地告诉标准库如何散列自定义类型,这样就不需要特殊处理来声明或初始化 unordered_maps 和 unordered_sets。 @T.C. 17.6.3.4 中的 Hash 要求中没有 result_typeargument_type 意味着该要求是针对标准库提供的特化,可能是针对希望将它们作为 unary_functions 处理的用户。我发现很难相信有一个病态的实施,例如unordered_set 可以与任何 Hash 一起正常工作,但明确断言,当且仅当它是 std::hash 专业化时,它的 hasher 必须具有这些类型。 [unord.hash] 的最后一个项目符号特别免除了用户定义的特化-“满足表达式h(k) 的要求,其中hhash&lt;Key&gt; 和@987654335 类型的对象@ 是 Key 类型的对象,除非 hash&lt;Key&gt; 是一个用户定义的专业化,它依赖于至少一个用户定义的类型,否则不应抛出异常”,所以在我看来,其他要求适用于所有专业化,无论是由图书馆还是用户提供(尽管如果它确实提出了这样的要求,老实说,这听起来像是一个缺陷)。 您能解释一下为什么需要template&lt;&gt; 吗?我做了与这个答案非常相似的事情并且它有效,但我根本没有使用模板,例如struct hash std::size_t operator () (const MyClass&amp; mc)... ;

以上是关于我的班级有一个 toString() 方法,我如何使用它在 std::unordered_set 中进行散列?的主要内容,如果未能解决你的问题,请参考以下文章

如何将复选框列表与我的班级中的属性绑定?

等待我的班级初始化(或如何等待 Future 完成)?

使 LINQ 扩展方法适用于我的班级

我如何从一个班级获取信息到另一个班级

为啥我的班级不使用我给它的值?

如何使 Javascript“toString”隐含方法文化意识