【问题标题】:error for hash function of pair of ints整数对散列函数的错误
【发布时间】:2014-01-02 15:32:33
【问题描述】:

我有以下带有unordered_map 成员的类,以及为pair<int,int> 定义的散列函数

class abc
{public :
    unordered_map < pair<int,int> , int > rules ;
    unsigned nodes;
    unsigned packet ;     
};

namespace std {
template <>
    class hash < std::pair< int,int> >{
    public :
        size_t operator()(const pair< int, int> &x ) const
        {
            size_t h =   std::hash<int>()(x.first) ^ std::hash<int>()(x.second);
            return  h ;
        }
    };
}

但我收到以下错误:

error: invalid use of incomplete type ‘struct std::hash<std::pair<int, int> >

error: declaration of ‘struct std::hash<std::pair<int, int> >

error: type ‘std::__detail::_Hashtable_ebo_helper<1, std::hash<std::pair<int, int> >, true>’ is not a direct base of ‘std::__detail::_Hash_code_base<std::pair<int, int>, std::pair<const std::pair<int, int>, int>, std::__detail::_Select1st, std::hash<std::pair<int, int> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, true>’

【问题讨论】:

  • 你应该转发declate template&lt;typename T&gt; class hash;
  • @Rapptz:前向声明是不够的。 OP 需要在class abc 之前定义专业化。
  • @JesseGood 是的,我现在明白了。
  • 如果两个程序员在同一个程序的两个组件中尝试这样做会发生什么?

标签: c++ c++11 hash stl unordered-map


【解决方案1】:

不幸的是,这个程序有未定义的行为。 C++11 §17.6.4.2.1:

只有当声明依赖于用户定义的类型并且特化满足原始模板的标准库要求并且没有明确禁止时,程序才能将任何标准库模板的模板特化添加到命名空间 std。

hash&lt;pair&lt;int,int&gt;&gt; 仅依赖于原始和标准库类型。这可以通过在命名空间std 之外定义您的哈希类并在您的地图声明中显式使用该哈希来轻松解决:

struct pairhash {
public:
  template <typename T, typename U>
  std::size_t operator()(const std::pair<T, U> &x) const
  {
    return std::hash<T>()(x.first) ^ std::hash<U>()(x.second);
  }
};

class abc {
  std::unordered_map<std::pair<int,int>, int, pairhash> rules;
};

编辑:我在这里使用 xor 来组合对成员的哈希值,因为我很懒,但要认真使用 xor is a fairly crappy hash combining function

【讨论】:

  • (我对 Jesse 发表的评论相同)std::hash&lt;T&gt;()(x.first) ^ std::hash&lt;T&gt;()(x.second); - 这是一种非常容易发生冲突的方法来散列 pair,因为具有两个相同值的每一对散列为 0,并且每一对 {a , b} 哈希与 {b, a} 相同。对于要求不高的用途,最好找到hash_combine 函数并使用它。
  • 等等,stl 不仅没有为std::pairs 实现std::hash,而且它也不允许你自己实现它?为什么?
  • @Claudiu:你可以自己实现,但前提是至少有一个模板参数是用户定义的类型。
  • @Sidak 存在一般规则是为了防止用户干扰库实现者和/或未来对标准的更改。例如,库实现可以提供std::hash&lt;std::pair&lt;T, U&gt;&gt; 的特化作为符合性扩展。或者 C++35 可能会定义这样的专业化。如果允许用户自己定义这样的专业化,那么在不破坏某些程序的情况下,两者都是不可能的。
  • @Casey,该死的! C ++ 35 ...大约再过两个十年?这太苛刻了...... :-)
【解决方案2】:

我更喜欢依靠std::hash&lt;uintmax_t&gt; 的标准实现来混合std::pair 的组件的哈希:

#include <functional>
#include <utility>

struct hash_pair final {
    template<class TFirst, class TSecond>
    size_t operator()(const std::pair<TFirst, TSecond>& p) const noexcept {
        uintmax_t hash = std::hash<TFirst>{}(p.first);
        hash <<= sizeof(uintmax_t) * 4;
        hash ^= std::hash<TSecond>{}(p.second);
        return std::hash<uintmax_t>{}(hash);
    }
};

【讨论】:

  • 散列
  • sizeof以字节为单位给出大小,但&lt;&lt; 采用要移位的位数。 en.cppreference.com/w/cpp/language/sizeof
  • 谢谢弗拉德。我忘记了它是以字节而不是比特来衡量的。所以目的是将第一个散列恰好移动 TFirst 大小的一半。旋转那个位数的哈希不是更好吗?否则我们会丢失第一个哈希的一半。
  • 这(例如 uintmax_t)将编译为 rol 32: hash = (hash > (sizeof(uintmax_t))
猜你喜欢
  • 2010-10-14
  • 2016-02-14
  • 2013-07-20
  • 2014-10-28
  • 2015-01-28
  • 1970-01-01
  • 1970-01-01
  • 2017-02-08
  • 1970-01-01
相关资源
最近更新 更多