簡體   English   中英

如何專門化 std::hash<Key> ::operator() 用於無序容器中的用戶定義類型?

[英]How to specialize std::hash<Key>::operator() for user-defined type in unordered containers?

要在std::unordered_set<Key>std::unordered_map<Key, Value>支持用戶定義的鍵類型,必須提供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;

只編寫帶有類型X默認散列的std::unordered_set<X>會更方便,就像編譯器和庫附帶的類型一樣。 咨詢后

  • C++ 標准草案 N3242 §20.8.12 [unord.hash] 和 §17.6.3.4 [hash.requirements],
  • Boost.Unordered
  • g++ include\\c++\\4.7.0\\bits\\functional_hash.h
  • VC10 include\\xfunctional
  • Stack Overflow 中的各種相關問題

似乎可以專門化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 
}                                                                             

鑒於對 C++11 的編譯器支持尚處於試驗階段——我沒有嘗試 Clang——,這些是我的問題:

  1. 將這樣的專業化添加到命名空間std是否合法? 我對此有復雜的感覺。

  2. 哪個std::hash<X>::operator()版本(如果有)符合 C++11 標准?

  3. 有沒有便攜的方法來做到這一點?

明確允許並鼓勵您向命名空間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 。)

*) 只要涉及的類型之一是用戶定義的,我想。

我的賭注是 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<X>()
    • 任何滿足簽名的綁定表達式 -

@Kerrek SB 已經涵蓋了 1) 和 3)。

2) 盡管 g++ 和 VC10 聲明std::hash<T>::operator()具有不同的簽名,但兩個庫實現都符合標准。

標准沒有指定std::hash<T>的成員。 它只是說每個這樣的特化必須滿足std::unordered_set等的第二個模板參數所需的相同“哈希”要求。 即:

  • 哈希類型H是一個函數對象,至少有一個參數類型Key
  • H是可復制構造的。
  • H是可破壞的。
  • 如果hHconst H類型的表達式,並且k是可轉換為(可能是constKey的類型的表達式,則h(k)是類型為size_t的有效表達式。
  • 如果hHconst H類型的表達式,而uKey類型的左值,則h(u)是類型為size_t不會修改u的有效表達式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM