Unordered_map,带有三个无符号字符作为键

时间:2014-03-04 15:35:20

标签: c++ hash hashtable unordered-map hash-function

我必须制作一个由以下键和值组成的unordered_map:

  • key:unsigned char,unsigned char,unsigned char

  • value:structure(int,int)

以下是我定义的代码:

namespace G {

typedef std::tuple< unsigned char, unsigned char , unsigned char> key_t;


struct IndexGuide
{
int index;
int SerialNo;

};

typedef std::unordered_map<const key_t,IndexGuide> GuideDouble;

}
using namespace G;

这段代码有什么问题,因为当我在linux终端上链接这些文件时。它给了我错误。错误的一部分如下

  

在函数`std :: __ detail :: _ Hash_code_base const,std :: pair const,G :: IndexGuide&gt;中,   std :: _ Select1st const,G :: IndexGuide&gt; &gt;中   的std :: equal_to   const&gt;,std :: hash const&gt;,std :: __ detail :: _ Mod_range_hashing,   的std :: __细节:: _ Default_ranged_hash,   false&gt; :: _ M_bucket_index(std :: __ detail :: _ Hash_node const,G :: IndexGuide&gt;,false&gt;   const *,unsigned int)const':

4 个答案:

答案 0 :(得分:2)

您必须提供一个哈希函数,告诉std::unordered_map如何从key_t生成哈希值。您可以通过hash模板仿函数的专业化来实现:

提供密钥:

struct Key{
    unsigned char  first;
    unsigned char second;
    unsigned char  third;

    bool operator==(const Key &other) const {
         return (first == other.first
            && second == other.second
            && third == other.third);
    }
};

专门化哈希模板:

namespace std {

  template <>
  struct hash< Key>
  {
    std::size_t operator()(const Key& k) const
    {
      using std::hash;

      // Compute individual hash values for first,
      // second and third and combine them using XOR
      // and bit shifting:

      return ((hash<char>()( k.first)
               ^ (hash<char>()( k.second) << 1)) >> 1)
               ^ (hash<int>()( k.third) << 1);
    }
  };

}

std::unordered_map< Key, IndexGuide> myMap;

正如我在评论中所说,你可以看看at this SO thread

答案 1 :(得分:2)

所以你的基本问题是你没有std::tuple的哈希 - 标准库目前默认没有。我认为这是一种疏忽。

您无法为std::tuple添加默认的哈希 - 无论是针对所有tuple还是针对您的特定列表 - 因为标准规定您不允许:

  

17.6.4.2.1 命名空间std [namespace.std] / 1如果C ++程序添加了声明或定义,则它的行为是未定义的   命名空间std或命名空间std中的命名空间,除非另有说明   指定。程序可以为任何程序添加模板专业化   标准库模板到命名空间std只有声明   取决于用户定义的类型,专业化符合   原始模板的标准库要求,而不是   明确禁止。

因此,您可以为tuple类型编写自定义哈希,并将其传递给unordered_map,或使用不仅仅为std::tuple的类型,或编写支持每个tuple的自定义哈希(包括递归tuple),同时还支持hash<T>查找非tuple s。第四个选项是编写自己的基于ADL的哈希系统并进行设置,以便具有有效std::hash特化的类型使用该系统,以及其他各种后备。

我将说明上面的第3个选项。

首先,您希望能够以不会导致许多虚假冲突的方式组合两个哈希值。

  inline std::size_t hash_result_combine(std::size_t lhs, std::size_t rhs)
  {
      return lhs ^( rhs + 0x9e3779b9 + (lhs<<6) + (lhs>>2));
  }

这需要两个hash输出,并将它们组合在一起。然后我们可以连续多次链接。 (从https://stackoverflow.com/a/7111460/1774667借来的常数)

接下来,一些索引样板。使用std::tuple的大多数通用工作都需要这样的东西:

  template<unsigned...>struct indexes{};
  template<unsigned Max, unsigned... Is>struct make_indexes:make_indexes<Max-1,Max-1,Is...> {};
  template<unsigned...Is>struct make_indexes<0,Is...>:indexes<Is...>{};

最后,一个几乎适用于所有类型的哈希类:

  struct my_hasher {
    template<typename... Args>
    std::size_t operator()(indexes<>, std::tuple<Args...> const& tup) const {
      return 0;
    }
    template<unsigned I, unsigned... Is,typename... Args>
    std::size_t operator()(indexes<I, Is...>,std::tuple<Args...> const& tup) const {
      return hash_result_combine( (*this)(std::get<I>(tup)), (*this)(indexes<Is...>, tup) );
    }
    template<typename... Args>
    std::size_t operator()(std::tuple<Args...> const& tup) const {
      return (*this)(make_indexes<sizeof...(Args)>(), tup);
    }
    template<typename T>
    std::size_t operator()(T const& t) const { return std::hash<T>()(t); }
  };

您可以将my_hasher传递给unordered_set代替hash<T> tuple s,tuple s包含元组,甚至是标量 - 它非常慷慨。

typedef std::unordered_map<const key_t,IndexGuide, my_hasher> GuideDouble;

现在我们正确哈希key_t

答案 2 :(得分:0)

使用哈希表实现

unordered_set 为了对您定义的类型使用unordered_set,您需要专门化该类型的hash<>模板,如下所示:

namespace std {
    template <> struct hash<key_t>
    {

        size_t operator()(const key_t & t) const
        {
            // the logic for hashing
        }
    };
}

答案 3 :(得分:0)

std :: tuple没有默认的哈希。

您可以使用此代码段Generic hash for tuples in unordered_map / unordered_set