如何在无序容器中为用户定义的类型专门化std :: hash <Key> :: operator()?

Ren*_*ter 95 c++ hash unordered-map unordered-set c++11

为了支持用户定义的键类型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;
Run Code Online (Sandbox Code Playgroud)

std::unordered_set<X> 使用类型的默认哈希来编写会更方便X,就像编译器和库中的类型一样.咨询后

似乎可以专门化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 
}                                                                             
Run Code Online (Sandbox Code Playgroud)

鉴于编译器对C++ 11的支持尚未实验 - 我没有尝试过Clang ---,这些是我的问题:

  1. 将这样的专业化添加到命名空间是否合法std?我对此感到复杂.

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

  3. 有可行的方法吗?

Ker*_* SB 121

您明确允许和鼓励增加来命名std*.添加哈希函数的正确(也是基本上唯一的)方法是这样的:

namespace std {
  template <> struct hash<Foo>
  {
    size_t operator()(const Foo & x) const
    {
      /* your code here, e.g. "return hash<int>()(x.value);" */
    }
  };
}
Run Code Online (Sandbox Code Playgroud)

(您可能考虑支持的其他流行专业是std::less,std::equal_tostd::swap.)

*)只要其中一个涉及的类型是用户定义的,我想.

  • 我并不反对,但是我们允许并鼓励标准中的哪些地方为std添加专业化? (3认同)
  • @Kerrek,我同意,但我希望有一个章节和经文参考标准中的一个地方.我发现允许在17.6.4.2.1进行注射的措辞,其中"除非另有说明",否则不允许进行注射,但我无法在4000+页面规范中找到"另有说明"的部分. (3认同)
  • 虽然这是可能的,你一般会建议这样做吗?我更喜欢实例化`unorder_map <eltype,hash,equality>`,以避免毁掉某人有趣的ADL业务.(**编辑**[Pete Becker对此主题的建议](http://bytes.com/topic/c/answers/820457-how-have-user-defined-hash-unordered_map)) (2认同)
  • @sehe:如果你有一个哈希算子,那可能是默认构造的,但为什么呢?(平等更容易,因为你只是实现了member-`operator ==`.)我的一般哲学是如果函数是自然的并且基本上是唯一的"正确"函数(如字典对比较),那么我将它添加到`std`.如果它是特殊的(如无序对比较),那么我将它特定于容器类型. (2认同)
  • @razeh [在这里你可以阅读](http://timepp.github.io/doc/cpp14/namespace.std.html)"只有在声明取决于程序时,程序才可以将任何标准库模板的模板特化添加到命名空间std在用户定义的类型上,专业化符合原始模板的标准库要求,并未明确禁止." 所以这个解决方案没问题. (2认同)

seh*_*ehe 6

我打赌将在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
}
Run Code Online (Sandbox Code Playgroud)

当然

  • hashX也可以是一个全局静态函数
  • 在第二种情况下,你可以通过
    • 老式的仿函数对象(struct Xhasher { size_t operator(const X&) const; };)
    • std::hash<X>()
    • 满足签名的任何绑定表达式 -