簡體   English   中英

是否有自定義類的unordered_set的默認哈希函數?

[英]Is there a default hash function for an unordered_set of a custom class?

我第一次使用std::unordered_set並且對哈希函數有疑問。 據我所知,如果你沒有指定一個哈希函數,它將默認為std::hash<Key>

我的一個mySet有一個mySet成員:

typedef std::unordered_set<MyClass> USetType;
USetType mySet;

當我嘗試構建時,我收到以下錯誤:

錯誤C2440:'type cast':無法從'const MyClass'轉換為'size_t'

如果要將unordered_set與自定義類一起使用,是否需要定義轉換函數(to size_t )? 有沒有辦法避免編寫自己的哈希函數並只使用默認值?

如果您沒有將自己的散列函數指定為模板參數,則默認為std::hash<MyClass> ,除非您定義它,否則它不存在。

最好在命名空間std定義你自己的std::hash專業化:

namespace std {
  template <>
  struct hash<MyClass>
  {
    typedef MyClass      argument_type;
    typedef std::size_t  result_type;

    result_type operator()(const MyClass & t) const
    {
       /* ..calculate hash value for t */
    }
  };
}

並確保在聲明哈希之前包含此代碼。 這樣,您可以將哈希聲明為std::unordered_set<MyClass> ,而無需進一步的模板參數。

你沒有指定MyClass里面的內容,但典型的情況是你的用戶定義類型只包含幾個簡單類型的成員,其中存在一個默認的哈希函數。 在這種情況下,您可能希望將各個類型的哈希值組合為整個組合的哈希值。 為此,Boost庫提供了一個名為hash_combine的函數。 當然,不能保證它在您的特定情況下能夠很好地工作(它取決於數據值的分布和碰撞的可能性),但它提供了一個好的且易於使用的起點。

下面是一個如何使用它的示例,假設MyClass包含兩個字符串成員:

#include <unordered_set>
#include <boost/functional/hash.hpp>

struct MyClass
{
  std::string _s1;
  std::string _s2;
};

namespace std {
  template <>
  struct hash<MyClass>
  {
    typedef MyClass      argument_type;
    typedef std::size_t  result_type;

    result_type operator()(const MyClass & t) const
    {
      std::size_t val { 0 };
      boost::hash_combine(val,t._s1);
      boost::hash_combine(val,t._s2);
      return val;
    }
  };
}

int main()
{
  std::unordered_set<MyClass> s;
  /* ... */
  return 0;
}

我想在擴大答案通過jogojapan給出。 正如CashCow對該答案的評論中所提到的,您還必須為MyClass 重載相等比較運算符operator== )或定義單獨的比較函數並將其提供給unordered_set 否則,您將收到另一條錯誤消息。 例如,VS 2013拋出:

錯誤C2678:二進制'==':找不到哪個運算符帶有'const MyClass'類型的左操作數(或者沒有可接受的轉換)

此外,您可以使用lambda表達式而不是定義哈希和比較函數。 如果你不想使用Boost ,那么你也可以手工編寫一個哈希函數 我明白,你想使用一些默認函數,但編譯器不知道如何為自定義類計算有意義的哈希。 但是,您可以對類的成員使用std::hash 如果你把所有東西放在一起,那么你的代碼可以寫成如下:

class MyClass {
public:
    int i;
    double d;
    std::string s;
};

int main()
{
    auto hash = [](const MyClass& mc){
        return (std::hash<int>()(mc.i) * 31 + std::hash<double>()(mc.d)) * 31 + std::hash<std::string>()(mc.s);
    };
    auto equal = [](const MyClass& mc1, const MyClass& mc2){
        return mc1.i == mc2.i && mc1.d == mc2.d && mc1.s == mc2.s;
    };
    std::unordered_set<MyClass, decltype(hash), decltype(equal)> mySet(8, hash, equal);

    return 0;
}

Ideone上的代碼

暫無
暫無

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

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