如何将 std::hash::operator() 专门化为无序容器中的用户定义类型?

发布于 2024-12-15 16:46:58 字数 1818 浏览 4 评论 0原文

支持 std::unordered_setstd::unordered_map 中用户定义的键类型 必须提供 operator==(Key, Key) 和一个哈希函子:

struct X { int id; /* ... */ };
bool operator==(X a, X b) { return a.id == b.id; }

struct MyHash {
  size_t operator()(const X& x) const { return std::hash<int>()(x.id); }
};

std::unordered_set<X, MyHash> s;

仅编写 std::unordered_set 会更方便 具有类型 X默认哈希, 就像编译器和库附带的类型一样。 查阅

似乎可以专门化 std: :hash::operator()

namespace std { // argh!
  template <>
  inline size_t 
  hash<X>::operator()(const X& x) const { return hash<int>()(x.id); } // works for MS VC10, but not for g++
  // or
  // hash<X>::operator()(X x) const { return hash<int>()(x.id); }     // works for g++ 4.7, but not for VC10 
}                                                                             

鉴于编译器对 C++11 的支持尚处于实验阶段——我没有尝试过 Clang——,这些是我的问题:

  1. 这样做是否合法添加这样一个专门化到命名空间std?我对此百感交集。

  2. 哪个 std::hash::operator() 版本(如果有)符合 C++11 标准?

  3. 是否有一种便携的方式来做到这一点?

To support user-defined key types in std::unordered_set<Key> and std::unordered_map<Key, Value>
one has to provide operator==(Key, Key) and a hash functor:

struct X { int id; /* ... */ };
bool operator==(X a, X b) { return a.id == b.id; }

struct MyHash {
  size_t operator()(const X& x) const { return std::hash<int>()(x.id); }
};

std::unordered_set<X, MyHash> s;

It would be more convenient to write just std::unordered_set<X>
with a default hash for type X,
like for types coming along with the compiler and library.
After consulting

it seems possible to specialize std::hash<X>::operator():

namespace std { // argh!
  template <>
  inline size_t 
  hash<X>::operator()(const X& x) const { return hash<int>()(x.id); } // works for MS VC10, but not for g++
  // or
  // hash<X>::operator()(X x) const { return hash<int>()(x.id); }     // works for g++ 4.7, but not for VC10 
}                                                                             

Given compiler support for C++11 is yet experimental---I did not try Clang---, these are my questions:

  1. Is it legal to add such a specialization to namespace std? I have mixed feelings about that.

  2. Which of the std::hash<X>::operator() versions, if any, is compliant with C++11 standard?

  3. Is there a portable way to do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不知在何时 2024-12-22 16:46:59

明确允许并鼓励您将专业化添加到命名空间std*。添加哈希函数的正确(并且基本上是唯一)方法是这样的:(

namespace std {
  template <> struct hash<Foo>
  {
    size_t operator()(const Foo & x) const
    {
      /* your code here, e.g. "return hash<int>()(x.value);" */
    }
  };
}

您可能考虑支持的其他流行专业化是 std::lessstd::equal_tostd::swap。)

*)只要所涉及的类型之一是用户定义的,我想。

You are expressly allowed and encouraged to add specializations to namespace std*. The correct (and basically only) way to add a hash function is this:

namespace std {
  template <> struct hash<Foo>
  {
    size_t operator()(const Foo & x) const
    {
      /* your code here, e.g. "return hash<int>()(x.value);" */
    }
  };
}

(Other popular specializations that you might consider supporting are std::less, std::equal_to and std::swap.)

*) as long as one of the involved types is user-defined, I suppose.

撩心不撩汉 2024-12-22 16:46:59

我的赌注是 unordered_map/unorder_set/... 类的 Hash 模板参数:

#include <unordered_set>
#include <functional>

struct X 
{
    int x, y;
    std::size_t gethash() const { return (x*39)^y; }
};

typedef std::unordered_set<X, std::size_t(*)(const X&)> Xunset;
typedef std::unordered_set<X, std::function<std::size_t(const X&)> > Xunset2;

int main()
{
    auto hashX = [](const X&x) { return x.gethash(); };

    Xunset  my_set (0, hashX);
    Xunset2 my_set2(0, hashX); // if you prefer a more flexible set typedef
}

当然,

  • hashX 也可以是全局静态函数
  • 在第二种情况下 ,你可以传递它
    • 老式仿函数对象 (struct Xhasher { size_t operator(const X&) const; };)
    • std::hash()
    • 任何满足签名的绑定表达式
      -

My bet would be on the Hash template argument for the unordered_map/unorder_set/... classes:

#include <unordered_set>
#include <functional>

struct X 
{
    int x, y;
    std::size_t gethash() const { return (x*39)^y; }
};

typedef std::unordered_set<X, std::size_t(*)(const X&)> Xunset;
typedef std::unordered_set<X, std::function<std::size_t(const X&)> > Xunset2;

int main()
{
    auto hashX = [](const X&x) { return x.gethash(); };

    Xunset  my_set (0, hashX);
    Xunset2 my_set2(0, hashX); // if you prefer a more flexible set typedef
}

Of course

  • hashX could just as well be a global static function
  • in the second case, you could pass that
    • the oldfashioned functor object (struct Xhasher { size_t operator(const X&) const; };)
    • std::hash<X>()
    • any bind expression satisfying the signature
      -
溺深海 2024-12-22 16:46:59

@Kerrek SB 已涵盖 1) 和 3)。

2) 尽管 g++ 和 VC10 使用不同的签名声明 std::hash::operator() ,但这两个库实现都符合标准。

标准未指定 std::hash 的成员。它只是说每个这样的专业化必须满足 std::unordered_set 的第二个模板参数所需的相同“哈希”要求等等。即:

  • 哈希类型H是一个函数对象,至少有一个参数类型Key
  • H 是可复制构造的。
  • H 是可破坏的。
  • 如果 hHconst H 类型的表达式,并且 k 是可转换为(可能是 constKey,则 h(k) 是类型为 size_t 的有效表达式。
  • 如果 hHconst H 类型的表达式,并且 u类型的左值Key,则 h(u) 是类型为 size_t 的有效表达式,不会修改 u

@Kerrek SB has covered 1) and 3).

2) Even though g++ and VC10 declare std::hash<T>::operator() with different signatures, both library implementations are Standard compliant.

The Standard does not specify the members of std::hash<T>. It just says that each such specialization must satisfy the same "Hash" requirements needed for the second template argument of std::unordered_set and so on. Namely:

  • Hash type H is a function object, with at least one argument type Key.
  • H is copy constructible.
  • H is destructible.
  • If h is an expression of type H or const H, and k is an expression of a type convertible to (possibly const) Key, then h(k) is a valid expression with type size_t.
  • If h is an expression of type H or const H, and u is an lvalue of type Key, then h(u) is a valid expression with type size_t which does not modify u.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文