c++ - unordered_map/unordered_set 中元组的通用哈希

标签 c++ c++11 tuples unordered-map unordered-set

为什么不 std::unordered_map<tuple<int, int>, string>只是 开箱即用? 必须为 tuple<int, int> 定义哈希函数是很乏味的,例如

template<> struct do_hash<tuple<int, int>>                               
{   size_t operator()(std::tuple<int, int> const& tt) const {...}  }; 

Building an unordered map with tuples as keys (Matthieu M.)展示了如何 为 boost::tuple 自动执行此操作.是否可以在不使用可变参数模板的情况下对 c++0x 元组执行此操作?

当然这应该在标准中:(

最佳答案

这适用于 gcc 4.5,允许所有包含标准可哈希类型的 c++0x 元组成为 unordered_mapunordered_set 事不宜迟。 (我把代码放在一个头文件中,然后直接包含它。)

该函数必须存在于 std 命名空间中,以便由 参数相关的名称查找 (ADL)。

有没有更简单的解决方案?

#include <tuple>
namespace std{
    namespace
    {

        // Code from boost
        // Reciprocal of the golden ratio helps spread entropy
        //     and handles duplicates.
        // See Mike Seymour in magic-numbers-in-boosthash-combine:
        //     http://stackoverflow.com/questions/4948780

        template <class T>
        inline void hash_combine(std::size_t& seed, T const& v)
        {
            seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        }

        // Recursive template code derived from Matthieu M.
        template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
        struct HashValueImpl
        {
          static void apply(size_t& seed, Tuple const& tuple)
          {
            HashValueImpl<Tuple, Index-1>::apply(seed, tuple);
            hash_combine(seed, std::get<Index>(tuple));
          }
        };

        template <class Tuple>
        struct HashValueImpl<Tuple,0>
        {
          static void apply(size_t& seed, Tuple const& tuple)
          {
            hash_combine(seed, std::get<0>(tuple));
          }
        };
    }

    template <typename ... TT>
    struct hash<std::tuple<TT...>> 
    {
        size_t
        operator()(std::tuple<TT...> const& tt) const
        {                                              
            size_t seed = 0;                             
            HashValueImpl<std::tuple<TT...> >::apply(seed, tt);    
            return seed;                                 
        }                                              

    };
}

标准合规代码

Yakk 指出在 std 命名空间中特化事物实际上是未定义的行为。如果你希望有一个符合标准的解决方案,那么你需要将所有这些代码移动到你自己的命名空间中,并放弃任何让 ADL 自动找到正确的哈希实现的想法。而不是:

unordered_set<tuple<double, int> > test_set;

你需要:

unordered_set<tuple<double, int>, hash_tuple::hash<tuple<double, int>>> test2;

其中 hash_tuple 是您自己的命名空间而不是 std::

为此,您首先必须在 hash_tuple 命名空间内声明一个哈希实现。这会将所有非元组类型转发到 std::hash:

namespace hash_tuple{

template <typename TT>
struct hash
{
    size_t
    operator()(TT const& tt) const
    {                                              
        return std::hash<TT>()(tt);                                 
    }                                              
};
}

确保 hash_combine 调用 hash_tuple::hash 而不是 std::hash

namespace hash_tuple{

namespace
    {
    template <class T>
    inline void hash_combine(std::size_t& seed, T const& v)
    {
        seed ^= hash_tuple::hash<T>()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
    }
}

然后包括所有其他先前的代码,但将其放在 namespace hash_tuple 而不是 std::

namespace hash_tuple{

    namespace
    {
        // Recursive template code derived from Matthieu M.
        template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
        struct HashValueImpl
        {
          static void apply(size_t& seed, Tuple const& tuple)
          {
            HashValueImpl<Tuple, Index-1>::apply(seed, tuple);
            hash_combine(seed, std::get<Index>(tuple));
          }
        };

        template <class Tuple>
        struct HashValueImpl<Tuple,0>
        {
          static void apply(size_t& seed, Tuple const& tuple)
          {
            hash_combine(seed, std::get<0>(tuple));
          }
        };
    }

    template <typename ... TT>
    struct hash<std::tuple<TT...>> 
    {
        size_t
        operator()(std::tuple<TT...> const& tt) const
        {                                              
            size_t seed = 0;                             
            HashValueImpl<std::tuple<TT...> >::apply(seed, tt);    
            return seed;                                 
        }                                              
    };

}

关于c++ - unordered_map/unordered_set 中元组的通用哈希,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32751963/

相关文章:

c++ - 在无法访问的语句中创建变量是否是明确定义的行为?

scala - “using”函数

c++ - 在另一个链表中插入一个链表

c++ - 为什么简单的 mysql++ 代码可以独立编译,但不能在项目中编译?

c++ - 在纯 C++ (C++11) 中扩充一个类/应用一个方面

c++ - 编译问题: building a Composite_Key variadic template class using tuples

python - 如何按这两个值对元组列表进行排序?

C++ 使用类模板创建多个结构

c++ - 找不到 Glog(缺少 : GLOG_INCLUDE_DIR GLOG_LIBRARY)

c++ - 是否必须定义所有静态类方法,即使未使用?