这在gcc 4.5上可行,它允许所有包含标准可哈希类型的c++0x元组成为
unordered_map
和
unordered_set
的成员,无需进一步操作。(我将代码放在头文件中,然后进行包含。)
该函数必须存活在std命名空间中,以便它被参数相关名称查找(ADL)所捕获。
是否有更简单的解决方案?
#include <tuple>
namespace std{
namespace
{
template <class T>
inline void hash_combine(std::size_t& seed, T const& v)
{
seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
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
{
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;
}
};
}