なぜ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 {...} };
タプルをキーとして使用した順序付けられていないマップの作成 (Matthieu M.)は、これをboost::Tuple
に対して自動化する方法を示しています。可変テンプレートを使用せずにc ++ 0xタプルに対してこれを行う方法はありますか?
確かにこれは標準にあるはずです:(
これはgcc 4.5で機能し、標準のハッシュ可能な型を含むすべてのc ++ 0xタプルをunordered_map
およびunordered_set
のメンバーにすることができます。 (コードをヘッダーファイルに入れてインクルードするだけです。)
関数は、引数に依存する名前のルックアップ(ADL)によって取得されるように、std名前空間に存在する必要があります。
より簡単な解決策はありますか?
#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;
}
};
}
#include <boost/functional/hash.hpp>
#include <Tuple>
namespace std
{
template<typename... T>
struct hash<Tuple<T...>>
{
size_t operator()(Tuple<T...> const& arg) const noexcept
{
return boost::hash_value(arg);
}
};
}
C++ 20では、 fold expression および generic lambdas を使用して、再帰なしでタプルのハッシュを計算できます。ハッシュを手動で組み合わせるのではなく、_std::hash<uintmax_t>
_に依存することを好みます。
_#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));
}
};
_
sizeof(uintmax_t) * 4 - 1
の_- 1
_はオプションですが、ハッシュの分布がわずかに改善されているようです。このクラスは、_std::Tuple
_および_std::pair
_の両方で使用できます。