Fix util::hash_map with std::pair as keys

This commit is contained in:
Nikita Lisitsa 2025-01-21 14:07:37 +03:00
parent 8a21e53ee1
commit a49be3b253

View file

@ -165,14 +165,20 @@ namespace psemek::util
}
};
template <typename T, typename Hash, typename Equal>
template <typename T, typename Hash, typename Equal, typename KeyProjector>
struct hash_table_impl
: Hash, Equal
: Hash, Equal, KeyProjector
{
template <typename H, typename K>
hash_table_impl(H && h, K && k)
: Hash(std::forward<H>(h))
, Equal(std::forward<K>(k))
hash_table_impl(Hash && hash, Equal && equal, KeyProjector && key_projector)
: Hash(std::move(hash))
, Equal(std::move(equal))
, KeyProjector(std::move(key_projector))
{}
hash_table_impl(Hash const & hash, Equal const & equal, KeyProjector const & key_projector)
: Hash(hash)
, Equal(equal)
, KeyProjector(key_projector)
{}
hash_table_impl(hash_table_impl && other)
@ -199,12 +205,13 @@ namespace psemek::util
Hash const & hash() const { return *this; }
Equal const & equal() const { return *this; }
KeyProjector const & key_projector() const { return *this; }
template <typename H>
std::pair<hash_table_iterator<T>, bool> insert(H && value)
{
ensure_capacity_for(size_ + 1);
std::size_t hash = this->hash()(value);
std::size_t hash = this->hash()(this->key_projector()(value));
return insert_impl(std::forward<H>(value), hash);
}
@ -332,7 +339,7 @@ namespace psemek::util
++size_;
return {storage_.iterator(index), true};
}
else if (entry.hash_equal(hash) && equal()(value, entry.value()))
else if (entry.hash_equal(hash) && equal()(key_projector()(value), key_projector()(entry.value())))
{
return {storage_.iterator(index), false};
}
@ -355,7 +362,7 @@ namespace psemek::util
{
return end();
}
else if (entry.hash_equal(hash) && equal()(key, entry.value()))
else if (entry.hash_equal(hash) && equal()(key, key_projector()(entry.value())))
{
return storage_.iterator(index);
}
@ -365,51 +372,21 @@ namespace psemek::util
}
};
template <typename Key, typename Value, typename Hash>
struct pair_hash
: Hash
struct id_key_projector
{
pair_hash(Hash const & hash)
: Hash(hash)
{}
template <typename Key1>
std::size_t operator()(Key1 const & key) const
template <typename Key>
Key const & operator() (Key const & key) const
{
return static_cast<Hash const &>(*this)(key);
}
template <typename Key1, typename Value1>
std::size_t operator()(std::pair<Key1, Value1> const & pair) const
{
return static_cast<Hash const &>(*this)(pair.first);
return key;
}
};
template <typename Key, typename Value, typename Equal>
struct pair_equal
: Equal
struct pair_key_projector
{
pair_equal(Equal const & equal)
: Equal(equal)
{}
template <typename Key1, typename Key2, typename Value2>
bool operator()(Key1 const & key1, std::pair<Key2, Value2> const & pair2) const
template <typename Key, typename Value>
Key const & operator() (std::pair<Key, Value> const & pair) const
{
return static_cast<Equal const &>(*this)(key1, pair2.first);
}
template <typename Key1, typename Value1, typename Key2>
bool operator()(std::pair<Key1, Value1> const & pair1, Key2 const & key2) const
{
return static_cast<Equal const &>(*this)(pair1.first, key2);
}
template <typename Key1, typename Value1, typename Key2, typename Value2>
bool operator()(std::pair<Key1, Value1> const & pair1, std::pair<Key2, Value2> const & pair2) const
{
return static_cast<Equal const &>(*this)(pair1.first, pair2.first);
return pair.first;
}
};
@ -420,12 +397,12 @@ namespace psemek::util
{
using iterator = detail::hash_table_iterator<T const>;
hash_set(Hash const & hash = {}, Equal const & equal = {})
: impl_(hash, equal)
hash_set(Hash && hash = {}, Equal && equal = {})
: impl_(hash, equal, {})
{}
hash_set(std::initializer_list<T> init, Hash const & hash = {}, Equal const & equal = {})
: impl_(hash, equal)
hash_set(std::initializer_list<T> init, Hash && hash = {}, Equal && equal = {})
: impl_(hash, equal, {})
{
for (auto & value : init)
insert(std::move(value));
@ -434,7 +411,7 @@ namespace psemek::util
hash_set(hash_set && other) = default;
hash_set(hash_set const & other) requires std::is_copy_constructible_v<T>
: impl_(other.impl_.hash(), other.impl_.equal())
: impl_(other.impl_.hash(), other.impl_.equal(), other.impl_.key_projector())
{
for (auto const & value : other)
insert(value);
@ -540,20 +517,20 @@ namespace psemek::util
}
private:
detail::hash_table_impl<T, Hash, Equal> impl_;
detail::hash_table_impl<T, Hash, Equal, detail::id_key_projector> impl_;
};
template <typename Key, typename Value, typename Hash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>>
template <typename Key, typename Value, typename KeyHash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>>
struct hash_map
{
using iterator = detail::hash_table_iterator<std::pair<Key const, Value>>;
hash_map(Hash const & hash = {}, KeyEqual const & equal = {})
: impl_(hash, equal)
hash_map(KeyHash && hash = {}, KeyEqual && equal = {})
: impl_(hash, equal, {})
{}
hash_map(std::initializer_list<std::pair<Key, Value>> init, Hash const & hash = {}, KeyEqual const & equal = {})
: impl_(hash, equal)
hash_map(std::initializer_list<std::pair<Key, Value>> init, KeyHash && hash = {}, KeyEqual && equal = {})
: impl_(hash, equal, {})
{
for (auto & pair : init)
insert(std::move(pair));
@ -562,7 +539,7 @@ namespace psemek::util
hash_map(hash_map && other) = default;
hash_map(hash_map const & other) requires std::is_copy_constructible_v<std::pair<Key, Value>>
: impl_(other.impl_.hash(), other.impl_.equal())
: impl_(other.impl_.hash(), other.impl_.equal(), other.impl_.key_projector())
{
for (auto const & pair : other)
insert({pair.first, pair.second});
@ -715,7 +692,7 @@ namespace psemek::util
}
private:
detail::hash_table_impl<std::pair<Key const, Value>, detail::pair_hash<Key, Value, Hash>, detail::pair_equal<Key, Value, KeyEqual>> impl_;
detail::hash_table_impl<std::pair<Key const, Value>, KeyHash, KeyEqual, detail::pair_key_projector> impl_;
};
}