C++ 无序映射/无序集中元组的通用哈希

C++ 无序映射/无序集中元组的通用哈希,c++,c++11,tuples,unordered-map,unordered-set,C++,C++11,Tuples,Unordered Map,Unordered Set,为什么std::unordered\u map 开箱即用? 必须为元组定义一个散列函数,例如 template<> struct do_hash<tuple<int, int>> { size_t operator()(std::tuple<int, int> const& tt) const {...} }; template struct do\u hash {si

为什么
std::unordered\u map
开箱即用? 必须为
元组定义一个散列函数,例如

template<> struct do_hash<tuple<int, int>>                               
{   size_t operator()(std::tuple<int, int> const& tt) const {...}  }; 
template struct do\u hash
{size_t operator()(std::tuple const&tt)const{…};
(Matthieu M.)展示了如何 为
boost::tuple
自动执行此操作。在不使用可变模板的情况下,是否可以对c++0x元组执行此操作


当然这应该在标准中:(

在我的C++0x草案中,
20.8.15
说哈希是专门用于内置类型的(包括指针,但似乎并不意味着取消对它们的引用)。它似乎还专门用于
错误
位集
唯一
共享
类型索引
字符串
u16string
u32string
wstring
向量
,以及
线程::id
(外观列表!)

我还没有使用C++0x变量,所以我的格式可能有点不对劲,但这些东西可能适用于所有元组

size_t hash_combiner(size_t left, size_t right) //replacable
{ return left + 0x9e3779b9 + (right<<6) + (right>>2);}

template<int index, class...types>
struct hash_impl {
    size_t operator()(size_t a, const std::tuple<types...>& t) const {
        typedef typename std::tuple_element<index, std::tuple<types...>>::type nexttype;
        hash_impl<index-1, types...> next;
        size_t b = std::hash<nexttype>()(std::get<index>(t));
        return next(hash_combiner(a, b), t); 
    }
};
template<class...types>
struct hash_impl<0, types...> {
    size_t operator()(size_t a, const std::tuple<types...>& t) const {
        typedef typename std::tuple_element<0, std::tuple<types...>>::type nexttype;
        size_t b = std::hash<nexttype>()(std::get<0>(t));
        return hash_combiner(a, b); 
    }
};

template<class...types>
struct tuple_hash<std::tuple<types...>> {
    size_t operator()(const std::tuple<types...>& t) {
        const size_t begin = std::tuple_size<std::tuple<types...>>::value-1;
        return hash_impl<begin, types...>()(0, t);
    }
}
size\u t hash\u组合器(size\u t left,size\u t right)//可重放
{返回左+0x9e3779b9+(右2);}
模板
结构hash_impl{
size_t运算符()(size_t a,const std::tuple&t)const{
typedef typename std::tuple_元素::type nexttype;
hash_impl next;
size_t b=std::hash()(std::get(t));
返回next(散列合并器(a,b),t);
}
};
模板
结构hash_impl{
size_t运算符()(size_t a,const std::tuple&t)const{
typedef typename std::tuple_元素::type nexttype;
size_t b=std::hash()(std::get(t));
返回hash_组合器(a,b);
}
};
模板
结构元组散列{
size\u t运算符()(常量std::tuple&t){
const size\u t begin=std::tuple\u size::value-1;
返回hash_impl()(0,t);
}
}


雅克观察到,从技术上讲,直接专门化
std::hash
是不允许的,因为我们专门化的标准库模板的声明不依赖于用户定义的类型。

在我的C++0x草稿中,
20.8.15
说hash专门用于内置类型(包括指针,但似乎并不意味着取消对它们的引用)。它似乎还专门用于
错误
位集
唯一
共享
类型索引
字符串
u16string
u32string
wstring
向量
,以及
线程::id
(外观列表!)

我还没有使用C++0x变量,所以我的格式可能有点不对劲,但这些东西可能适用于所有元组

size_t hash_combiner(size_t left, size_t right) //replacable
{ return left + 0x9e3779b9 + (right<<6) + (right>>2);}

template<int index, class...types>
struct hash_impl {
    size_t operator()(size_t a, const std::tuple<types...>& t) const {
        typedef typename std::tuple_element<index, std::tuple<types...>>::type nexttype;
        hash_impl<index-1, types...> next;
        size_t b = std::hash<nexttype>()(std::get<index>(t));
        return next(hash_combiner(a, b), t); 
    }
};
template<class...types>
struct hash_impl<0, types...> {
    size_t operator()(size_t a, const std::tuple<types...>& t) const {
        typedef typename std::tuple_element<0, std::tuple<types...>>::type nexttype;
        size_t b = std::hash<nexttype>()(std::get<0>(t));
        return hash_combiner(a, b); 
    }
};

template<class...types>
struct tuple_hash<std::tuple<types...>> {
    size_t operator()(const std::tuple<types...>& t) {
        const size_t begin = std::tuple_size<std::tuple<types...>>::value-1;
        return hash_impl<begin, types...>()(0, t);
    }
}
size\u t hash\u组合器(size\u t left,size\u t right)//可重放
{返回左+0x9e3779b9+(右2);}
模板
结构hash_impl{
size_t运算符()(size_t a,const std::tuple&t)const{
typedef typename std::tuple_元素::type nexttype;
hash_impl next;
size_t b=std::hash()(std::get(t));
返回next(散列合并器(a,b),t);
}
};
模板
结构hash_impl{
size_t运算符()(size_t a,const std::tuple&t)const{
typedef typename std::tuple_元素::type nexttype;
size_t b=std::hash()(std::get(t));
返回hash_组合器(a,b);
}
};
模板
结构元组散列{
size\u t运算符()(常量std::tuple&t){
const size\u t begin=std::tuple\u size::value-1;
返回hash_impl()(0,t);
}
}


雅克观察到,从技术上讲,直接专门化
std::hash
是不允许的,因为我们专门化了一个标准库模板,其声明不依赖于用户定义的类型。

这在gcc 4.5上起作用,允许包含标准哈希类型的所有c++0x元组成为
unordered_map
unordered_set
,无需进一步ado。 (我把代码放在一个头文件中,只包含它。)

函数必须位于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;                                 
        }                                              

    };
}
确保
hash\u combine
调用
hash\u 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);
    }
}

这适用于GCC4.5,允许包含标准哈希类型的所有c++0x元组成为
unordered_map
unordered_set
,无需进一步ado。 (我把代码放在一个头文件中,只包含它。)

函数必须位于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;                                 
        }                                              

    };
}
确保
hash\u combine
调用
hash\u 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);
    }
}
#包括
#包括
名称空间标准
{
模板
结构散列
{
size_t运算符()(元组常量和参数)常量noexcept
{
返回boost::hash_值(arg);
}
};
}
#包括
#包括
名称空间标准
{
模板
结构散列
{
size_t运算符()(元组常量和参数)常量noexcept
{
返回boost::hash_值(arg);
}
};
}

使用C++20,可以使用和计算元组的哈希而无需递归。我更喜欢依赖
std::hash
,而不是手动组合哈希:

#include <cinttypes>
#include <cstddef>
#include <functional>
#include <tuple>

class hash_tuple {
    template<class T>
    struct component {
        const T& value;
        component(const T& value) : value(value) {}
        uintmax_t operator,(uintmax_t n) const {
            n ^= std::hash<T>()(value);
            n ^= n << (sizeof(uintmax_t) * 4 - 1);
            return n ^ std::hash<uintmax_t>()(n);
        }
    };

public:
    template<class Tuple>
    size_t operator()(const Tuple& tuple) const {
        return std::hash<uintmax_t>()(
            std::apply([](const auto& ... xs) { return (component(xs), ..., 0); }, tuple));
    }
};
#包括
#包括
#包括
#包括
类哈希元组{
模板
结构组件{
常数T&value;
组件(常量T和值):值(值){}
uintmax_t运算符,(uintmax_t n)常数{
n^=std::hash()(值);

n^=n使用C++20,可以使用和计算元组的哈希,而无需递归。我更喜欢依赖
std::hash
,而不是手动组合哈希:

#include <cinttypes>
#include <cstddef>
#include <functional>
#include <tuple>

class hash_tuple {
    template<class T>
    struct component {
        const T& value;
        component(const T& value) : value(value) {}
        uintmax_t operator,(uintmax_t n) const {
            n ^= std::hash<T>()(value);
            n ^= n << (sizeof(uintmax_t) * 4 - 1);
            return n ^ std::hash<uintmax_t>()(n);
        }
    };

public:
    template<class Tuple>
    size_t operator()(const Tuple& tuple) const {
        return std::hash<uintmax_t>()(
            std::apply([](const auto& ... xs) { return (component(xs), ..., 0); }, tuple));
    }
};
#包括
#包括
#包括
#包括
类哈希元组{
模板
结构组件{
常数T&value;
组件(常数
#include <cinttypes>
#include <cstddef>
#include <functional>
#include <tuple>

class hash_tuple {
    template<class T>
    struct component {
        const T& value;
        component(const T& value) : value(value) {}
        uintmax_t operator,(uintmax_t n) const {
            n ^= std::hash<T>()(value);
            n ^= n << (sizeof(uintmax_t) * 4 - 1);
            return n ^ std::hash<uintmax_t>()(n);
        }
    };

public:
    template<class Tuple>
    size_t operator()(const Tuple& tuple) const {
        return std::hash<uintmax_t>()(
            std::apply([](const auto& ... xs) { return (component(xs), ..., 0); }, tuple));
    }
};