libstdc++: Remove _Equality base class from _Hashtable

libstdc++-v3/ChangeLog:

	* include/bits/hashtable.h (_Hashtable): Remove _Equality base
	class.
	(_Hashtable::_M_equal): Define equality comparison here instead
	of in _Equality::_M_equal.
	* include/bits/hashtable_policy.h (_Equality): Remove.
This commit is contained in:
Jonathan Wakely 2024-11-05 22:39:43 +00:00 committed by Jonathan Wakely
parent 0935d0d6e6
commit 247e82c72f
No known key found for this signature in database
2 changed files with 94 additions and 164 deletions

View File

@ -168,8 +168,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* not throw and this is enforced by a static assertion.
*
* Functionality is implemented by decomposition into base classes,
* where the derived _Hashtable class is used in _Map_base,
* _Rehash_base, and _Equality base classes to access the
* where the derived _Hashtable class is used in _Map_base and
* _Rehash_base base classes to access the
* "this" pointer. _Hashtable_base is used in the base classes as a
* non-recursive, fully-completed-type so that detailed nested type
* information, such as iterator type and node type, can be
@ -181,7 +181,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* - __detail::_Hashtable_base
* - __detail::_Map_base
* - __detail::_Rehash_base
* - __detail::_Equality
*/
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
@ -196,9 +195,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
public __detail::_Rehash_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_Hash, _RangeHash, _Unused,
_RehashPolicy, _Traits>,
public __detail::_Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_Hash, _RangeHash, _Unused,
_RehashPolicy, _Traits>,
private __detail::_Hashtable_alloc<
__alloc_rebind<_Alloc,
__detail::_Hash_node<_Value,
@ -293,10 +289,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Hash, _RangeHash, _Unused,
_RehashPolicy, _Traits>;
using __eq_base = __detail::_Equality<_Key, _Value, _Alloc, _ExtractKey,
_Equal, _Hash, _RangeHash, _Unused,
_RehashPolicy, _Traits>;
using __node_builder_t = __detail::_NodeBuilder<_ExtractKey>;
// Simple RAII type for managing a node containing an element
@ -353,13 +345,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
bool _Unique_keysa>
friend struct __detail::_Map_base;
template<typename _Keya, typename _Valuea, typename _Alloca,
typename _ExtractKeya, typename _Equala,
typename _Hasha, typename _RangeHasha, typename _Unuseda,
typename _RehashPolicya, typename _Traitsa,
bool _Unique_keysa>
friend struct __detail::_Equality;
public:
using size_type = typename __hashtable_base::size_type;
using difference_type = typename __hashtable_base::difference_type;
@ -1300,6 +1285,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#endif // C++17 __glibcxx_node_extract
bool
_M_equal(const _Hashtable& __other) const;
private:
// Helper rehash method used when keys are unique.
void _M_rehash(size_type __bkt_count, true_type __uks);
@ -2798,6 +2786,95 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_M_buckets = __new_buckets;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
// This is for implementing equality comparison for unordered containers,
// per N3068, by John Lakos and Pablo Halpern.
// Algorithmically, we follow closely the reference implementations therein.
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _Hash, typename _RangeHash, typename _Unused,
typename _RehashPolicy, typename _Traits>
bool
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
_M_equal(const _Hashtable& __other) const
{
if (size() != __other.size())
return false;
if constexpr (__unique_keys::value)
for (auto __x_n = _M_begin(); __x_n; __x_n = __x_n->_M_next())
{
std::size_t __ybkt = __other._M_bucket_index(*__x_n);
auto __prev_n = __other._M_buckets[__ybkt];
if (!__prev_n)
return false;
for (__node_ptr __n = static_cast<__node_ptr>(__prev_n->_M_nxt);;
__n = __n->_M_next())
{
if (__n->_M_v() == __x_n->_M_v())
break;
if (!__n->_M_nxt
|| __other._M_bucket_index(*__n->_M_next()) != __ybkt)
return false;
}
}
else // non-unique keys
for (auto __x_n = _M_begin(); __x_n;)
{
std::size_t __x_count = 1;
auto __x_n_end = __x_n->_M_next();
for (; __x_n_end
&& key_eq()(_ExtractKey{}(__x_n->_M_v()),
_ExtractKey{}(__x_n_end->_M_v()));
__x_n_end = __x_n_end->_M_next())
++__x_count;
std::size_t __ybkt = __other._M_bucket_index(*__x_n);
auto __y_prev_n = __other._M_buckets[__ybkt];
if (!__y_prev_n)
return false;
__node_ptr __y_n = static_cast<__node_ptr>(__y_prev_n->_M_nxt);
for (;;)
{
if (key_eq()(_ExtractKey{}(__y_n->_M_v()),
_ExtractKey{}(__x_n->_M_v())))
break;
auto __y_ref_n = __y_n;
for (__y_n = __y_n->_M_next(); __y_n; __y_n = __y_n->_M_next())
if (!__other._M_node_equals(*__y_ref_n, *__y_n))
break;
if (!__y_n || __other._M_bucket_index(*__y_n) != __ybkt)
return false;
}
auto __y_n_end = __y_n;
for (; __y_n_end; __y_n_end = __y_n_end->_M_next())
if (--__x_count == 0)
break;
if (__x_count != 0)
return false;
const_iterator __itx(__x_n), __itx_end(__x_n_end);
const_iterator __ity(__y_n);
if (!std::is_permutation(__itx, __itx_end, __ity))
return false;
__x_n = __x_n_end;
}
return true;
}
#pragma GCC diagnostic pop
#if __cplusplus > 201402L
template<typename, typename, typename> class _Hash_merge_helper { };
#endif // C++17

View File

@ -1568,153 +1568,6 @@ namespace __detail
_M_eq() const { return _EqualEBO::_M_cget(); }
};
/**
* Primary class template _Equality.
*
* This is for implementing equality comparison for unordered
* containers, per N3068, by John Lakos and Pablo Halpern.
* Algorithmically, we follow closely the reference implementations
* therein.
*/
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _Hash, typename _RangeHash, typename _Unused,
typename _RehashPolicy, typename _Traits,
bool _Unique_keys = _Traits::__unique_keys::value>
struct _Equality;
/// unordered_map and unordered_set specializations.
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _Hash, typename _RangeHash, typename _Unused,
typename _RehashPolicy, typename _Traits>
struct _Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits, true>
{
using __hashtable = _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_Hash, _RangeHash, _Unused,
_RehashPolicy, _Traits>;
bool
_M_equal(const __hashtable&) const;
};
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _Hash, typename _RangeHash, typename _Unused,
typename _RehashPolicy, typename _Traits>
bool
_Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits, true>::
_M_equal(const __hashtable& __other) const
{
using __node_ptr = typename __hashtable::__node_ptr;
const __hashtable* __this = static_cast<const __hashtable*>(this);
if (__this->size() != __other.size())
return false;
for (auto __x_n = __this->_M_begin(); __x_n; __x_n = __x_n->_M_next())
{
std::size_t __ybkt = __other._M_bucket_index(*__x_n);
auto __prev_n = __other._M_buckets[__ybkt];
if (!__prev_n)
return false;
for (__node_ptr __n = static_cast<__node_ptr>(__prev_n->_M_nxt);;
__n = __n->_M_next())
{
if (__n->_M_v() == __x_n->_M_v())
break;
if (!__n->_M_nxt
|| __other._M_bucket_index(*__n->_M_next()) != __ybkt)
return false;
}
}
return true;
}
/// unordered_multiset and unordered_multimap specializations.
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _Hash, typename _RangeHash, typename _Unused,
typename _RehashPolicy, typename _Traits>
struct _Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits, false>
{
using __hashtable = _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_Hash, _RangeHash, _Unused,
_RehashPolicy, _Traits>;
bool
_M_equal(const __hashtable&) const;
};
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _Hash, typename _RangeHash, typename _Unused,
typename _RehashPolicy, typename _Traits>
bool
_Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits, false>::
_M_equal(const __hashtable& __other) const
{
using __node_ptr = typename __hashtable::__node_ptr;
using const_iterator = typename __hashtable::const_iterator;
const __hashtable* __this = static_cast<const __hashtable*>(this);
if (__this->size() != __other.size())
return false;
for (auto __x_n = __this->_M_begin(); __x_n;)
{
std::size_t __x_count = 1;
auto __x_n_end = __x_n->_M_next();
for (; __x_n_end
&& __this->key_eq()(_ExtractKey{}(__x_n->_M_v()),
_ExtractKey{}(__x_n_end->_M_v()));
__x_n_end = __x_n_end->_M_next())
++__x_count;
std::size_t __ybkt = __other._M_bucket_index(*__x_n);
auto __y_prev_n = __other._M_buckets[__ybkt];
if (!__y_prev_n)
return false;
__node_ptr __y_n = static_cast<__node_ptr>(__y_prev_n->_M_nxt);
for (;;)
{
if (__this->key_eq()(_ExtractKey{}(__y_n->_M_v()),
_ExtractKey{}(__x_n->_M_v())))
break;
auto __y_ref_n = __y_n;
for (__y_n = __y_n->_M_next(); __y_n; __y_n = __y_n->_M_next())
if (!__other._M_node_equals(*__y_ref_n, *__y_n))
break;
if (!__y_n || __other._M_bucket_index(*__y_n) != __ybkt)
return false;
}
auto __y_n_end = __y_n;
for (; __y_n_end; __y_n_end = __y_n_end->_M_next())
if (--__x_count == 0)
break;
if (__x_count != 0)
return false;
const_iterator __itx(__x_n), __itx_end(__x_n_end);
const_iterator __ity(__y_n);
if (!std::is_permutation(__itx, __itx_end, __ity))
return false;
__x_n = __x_n_end;
}
return true;
}
/**
* This type deals with all allocation and keeps an allocator instance
* through inheritance to benefit from EBO when possible.