我正在使用一个有序的集合,声明如下:

std::set<std::pair<const std::string, const myClass *> > myset;

在对我使用set,的方式进行了一些分析之后set,我得出结论, unordered_set将是一个更明智的选择。 但是当我将std::set更改为std::unordered_set ,我的编译器(g++ 4.8.1)收到了大量错误消息,抱怨

invalid use of incomplete type struct std::hash<std::pair<const std::basic_string<char>, const myClass * > >

我想通了, std::hash不知道如何处理这是一个类型std::pair ,尽管这两种类型的弥补了pair为每个哈希的。 我认为一对整数的哈希函数的错误包含有关 C++11 标准的相关信息,这些信息解释了为什么会出错。 (对于 g++ 为此发出的难以穿透的错误文本墙,没有很好的解释。)

在我看来

std::hash<std::pair<T1, T2>> hasher(make_pair(x,y))
  = some_func(std::hash<T1>hasher(x), std::hash<T2>hasher(y) )

其中some_func()可以像 XOR 一样简单(或不是;请参阅为什么 XOR 是组合散列的默认方式?

标准是否有充分的理由不要求std::hash知道如何为一个对象构造一个散列值,该对象是一pair可散列的类型?

#1楼 票数:1

原因很简单,它没有被添加到标准中。 对其他结构(如tuple进行散列也是如此。

当事物足够好时,往往会被添加到标准中,而不是当它们完美时,因为完美是善的敌人。 std::hash更多专业化不是会破坏代码的东西(经常),因此添加新的东西相对无害。

无论如何,为此,我们可以编写自己的哈希扩展器。 举个例子:

namespace hashers {
  constexpr size_t hash_combine( size_t, size_t ); // steal from boost, or write your own
  constexpr size_t hash_combine( size_t a ) { return a; }
  constexpr size_t hash_combine() { return 0; }
  template<class...Sizes>
  constexpr size_t hash_combine( size_t a, size_t b, Sizes... sizes ) {
    return hash_combine( hash_combine(a,b), sizes... );
  }

  template<class T=void> struct hash;

  template<class A, class B>
  constexpr size_t custom_hash( std::pair<A,B> const& p ) {
    return hash_combine( hash<size_t>{}(2), hash<std::decay_t<A>>{}(p.first), hash<std::decay_t<B>>{}(p.second) );
  }
  template<class...Ts, size_t...Is>
  constexpr size_t custom_hash( std::index_sequence<Is...>, std::tuple<Ts...> const& p ) {
    return hash_combine( hash<size_t>{}(sizeof...(Ts)), hash<std::decay_t<Ts>>{}(std::get<Is>(p))... );
  }
  template<class...Ts>
  constexpr size_t custom_hash( std::tuple<Ts...> const& p ) {
    return custom_hash( std::index_sequence_for<Ts...>{}, p );
  }
  template<class T0, class C>
  constexpr size_t custom_hash_container( size_t n, C const& c) {
    size_t retval = hash<size_t>{}(n);
    for( auto&& x : c)
      retval = hash_combine( retval, hash<T>{}(x) );
    return retval;
  }
  template<class T0, class C>
  constexpr size_t custom_hash_container( C const& c) {
    return custom_hash_container( c.size(), c );
  }
  template<class T, class...Ts>
  size_t custom_hash( std::vector<T, Ts...> const& v ) {
    return custom_hash_container<T>(v);
  }
  template<class T, class...Ts>
  size_t custom_hash( std::basic_string<T, Ts...> const& v ) {
    return custom_hash_container<T>(v);
  }
  template<class T, size_t n>
  constexpr size_t custom_hash( std::array<T, n> const& v ) {
    return custom_hash_container<T>(n, v);
  }
  template<class T, size_t n>
  constexpr size_t custom_hash( T (const& v)[n] ) {
    return custom_hash_container<T>(n, v);
  }
  // etc -- list, deque, map, unordered map, whatever you want to support
  namespace details {
    template<class T, class=void>
    struct hash : std::hash<T> {};
    using hashers::custom_hash;
    template<class T>
    struct hash<T,decltype(void(
      custom_hash(declval<T const&>())
    )) {
      constexpr size_t operator()(T const& t)const {
        return custom_hash(t);
      }
    };
  }
  template<class T>
  struct hash : details::hash<T> {};
  template<>
  struct hash<void> {
    template<class T>
    constexpr size_t operator()(T const& t)const { return hash<T>{}(t); }
  }
}

现在hashers::hash<T>将递归使用 ADL 查找的custom_hash函数或std::hash如果失败,对T及其组件进行散列,并且hashers::hash<>是一个通用散列器,它会尝试散列任何传递给它的东西。

代码可能无法按所示编译。

我选择散列所有容器和元组作为散列它们的长度,然后散列它们的内容组合。 作为副作用, array<int, 3>散列与tuple<int,int,int>tuple<int,int>散列与pair<int,int>std::vector<char>{'a','b','c', '\\0'}散列相同std::vector<char>{'a','b','c', '\\0'}散列与"abc"相同,我认为这是一个不错的属性。 空数组/元组/向量/等哈希像size_t(0)

您可以通过简单地覆盖custom_hash类型的命名空间中的custom_hash来为您自己的类型扩展上述系统,或者专门使用std::hash<X>hashers::hash<X>来执行您的自定义哈希(我会去使用std::hash的原则是最少让我感到惊讶)。 对于高级用途,您可以使用 SFINAE 专门化hashers::details::hash<X,void> ,但我会说为custom_hash做它。

  ask by jzions translate from so

未解决问题?本站智能推荐:

3回复

为什么std::hash不专用于char*?

为什么C ++标准没有指定std::hash<T>专门用于char* , const char* , unsigned char* , const unsigned char*等? 即,它将散列C字符串的内容,直到找到终止空值。 将我自己的特化注入我自己的代码的std命名空间有
1回复

专攻std::hash对于依赖类型

我已经定义了这个模板类结构:template<typename T> struct Outer { struct Inner { /* ...some stuff... */ };}; 我想将Inner对象放入unordered_map (实际上,不是直接它们而是它们的容器,因此直
3回复

可以使用std::hash来散列函数指针吗?

可以使用C ++ 11 std::hash类型来散列函数指针吗? hash部分特化定义为 但由于函数指针与C ++中的其他指针类型不同(例如它们不能转换为void* ),我不确定将它用于int(*)()或void(*)(int, int)等类型是否安全void(*)(int, int) 。
1回复

std::hash和/或boost::hash的目的是什么?

为什么不提供哈希函数而不指定引用的任何实现,也没有指定引用的算法(md5,sha256等)? 数据结构也有类似的功能,例如符合C ++标准的std::unordered_map/set/multimap/multiset::hash_function 。 所以我没有得到的是: 为什
1回复

std::hash算法和大小

我正在使用C ++ 11和std :: hash算法。 我想知道,使用了什么实际的哈希实现? 我会假设MD5或SHA,但我不能从互联网中挖掘任何信息。 另外,我想知道散列的实际返回位宽,因为我必须将它存储在MySQL中。 最后,是否最好使用std :: hash,比如说其他一些库如c
1回复

如何将std::hash专门化为其他库中的类型

所以我使用的库有一个枚举(比如它叫做LibEnum )。 我需要一个LibEnum的std::unordered_set ,但是我得到了编译错误,它没有专门的std::hash 。 我可以很容易地写它并且只返回值的数量(第一个元素是0,第二个1等),但是我应该把它放在哪个专门化以及它应该是什
3回复

std::hash保证在stdlib发行版中是相同的

如果我使用libstdc++做了std::hash ,然后在即将推出的C++11 VS 2012库中做了一个 - 它们会匹配吗? 我假设哈希实现不是C ++规范的一部分,可以根据分布而变化?
2回复

在C++11中使用boost::hash_value定义std::hash

有没有一种简单的方法可以对C ++ 11和Boost进行以下操作: 只要<functional>可用,请使用std::hash的标准定义 在缺少std::hash但在<boost/functional/hash.hpp>可用boost::hash_value