diff options
author | François Dumont <fdumont@gcc.gnu.org> | 2021-10-13 22:04:32 +0200 |
---|---|---|
committer | François Dumont <fdumont@gcc.gnu.org> | 2021-11-09 21:50:17 +0100 |
commit | f4b4ce152a06578d94ae7630cffd655d590b2857 (patch) | |
tree | 46615606217dc6b64d3370cb652c96537abfd567 /libstdc++-v3/include | |
parent | f7844b6a777cb47fec7208d7bd594f6ce66f1daa (diff) | |
download | gcc-f4b4ce152a06578d94ae7630cffd655d590b2857.zip gcc-f4b4ce152a06578d94ae7630cffd655d590b2857.tar.gz gcc-f4b4ce152a06578d94ae7630cffd655d590b2857.tar.bz2 |
libstdc++: [_GLIBCXX_DEBUG] Implement unordered container merge
The _GLIBCXX_DEBUG unordered containers need a dedicated merge implementation
so that any existing iterator on the transfered nodes is properly invalidated.
Add typedef/using declarations for everything used as-is from normal implementation.
libstdc++-v3/ChangeLog:
* include/bits/hashtable_policy.h (__distance_fw): Replace class keyword with
typename.
* include/bits/hashtable.h (_Hashtable<>::_M_merge_unique): Remove noexcept
qualification. Use const_iterator for node extraction/reinsert.
(_Hashtable<>::_M_merge_multi): Likewise. Compute new hash code before extract.
* include/debug/safe_container.h (_Safe_container<>): Make all methods
protected.
* include/debug/safe_unordered_container.h
(_Safe_unordered_container<>::_UContInvalidatePred<_ExtractKey, _Source>): New.
(_Safe_unordered_container<>::_UMContInvalidatePred<_ExtractKey, _Source>): New.
(_Safe_unordered_container<>::_UContMergeGuard<_Source, _InvalidatePred>): New.
(_Safe_unordered_container<>::_S_uc_guard<_ExtractKey, _Source>): New.
(_Safe_unordered_container<>::_S_umc_guard<_ExtractKey, _Source>): New.
(_Safe_unordered_container<>::_M_invalide_all): Make public.
(_Safe_unordered_container<>::_M_invalide_if): Likewise.
(_Safe_unordered_container<>::_M_invalide_local_if): Likewise.
* include/debug/unordered_map
(unordered_map<>::mapped_type, pointer, const_pointer): New typedef.
(unordered_map<>::reference, const_reference, difference_type): New typedef.
(unordered_map<>::get_allocator, empty, size, max_size): Add usings.
(unordered_map<>::bucket_count, max_bucket_count, bucket): Add usings.
(unordered_map<>::hash_function, key_equal, count, contains): Add usings.
(unordered_map<>::operator[], at, rehash, reserve): Add usings.
(unordered_map<>::merge): New.
(unordered_multimap<>::mapped_type, pointer, const_pointer): New typedef.
(unordered_multimap<>::reference, const_reference, difference_type): New typedef.
(unordered_multimap<>::get_allocator, empty, size, max_size): Add usings.
(unordered_multimap<>::bucket_count, max_bucket_count, bucket): Add usings.
(unordered_multimap<>::hash_function, key_equal, count, contains): Add usings.
(unordered_multimap<>::rehash, reserve): Add usings.
(unordered_multimap<>::merge): New.
* include/debug/unordered_set
(unordered_set<>::mapped_type, pointer, const_pointer): New typedef.
(unordered_set<>::reference, const_reference, difference_type): New typedef.
(unordered_set<>::get_allocator, empty, size, max_size): Add usings.
(unordered_set<>::bucket_count, max_bucket_count, bucket): Add usings.
(unordered_set<>::hash_function, key_equal, count, contains): Add usings.
(unordered_set<>::rehash, reserve): Add usings.
(unordered_set<>::merge): New.
(unordered_multiset<>::mapped_type, pointer, const_pointer): New typedef.
(unordered_multiset<>::reference, const_reference, difference_type): New typedef.
(unordered_multiset<>::get_allocator, empty, size, max_size): Add usings.
(unordered_multiset<>::bucket_count, max_bucket_count, bucket): Add usings.
(unordered_multiset<>::hash_function, key_equal, count, contains): Add usings.
(unordered_multiset<>::rehash, reserve): Add usings.
(unordered_multiset<>::merge): New.
* testsuite/23_containers/unordered_map/debug/merge1_neg.cc: New test.
* testsuite/23_containers/unordered_map/debug/merge2_neg.cc: New test.
* testsuite/23_containers/unordered_map/debug/merge3_neg.cc: New test.
* testsuite/23_containers/unordered_map/debug/merge4_neg.cc: New test.
* testsuite/23_containers/unordered_multimap/debug/merge1_neg.cc: New test.
* testsuite/23_containers/unordered_multimap/debug/merge2_neg.cc: New test.
* testsuite/23_containers/unordered_multimap/debug/merge3_neg.cc: New test.
* testsuite/23_containers/unordered_multimap/debug/merge4_neg.cc: New test.
* testsuite/23_containers/unordered_multiset/debug/merge1_neg.cc: New test.
* testsuite/23_containers/unordered_multiset/debug/merge2_neg.cc: New test.
* testsuite/23_containers/unordered_multiset/debug/merge3_neg.cc: New test.
* testsuite/23_containers/unordered_multiset/debug/merge4_neg.cc: New test.
* testsuite/23_containers/unordered_set/debug/merge1_neg.cc: New test.
* testsuite/23_containers/unordered_set/debug/merge2_neg.cc: New test.
* testsuite/23_containers/unordered_set/debug/merge3_neg.cc: New test.
* testsuite/23_containers/unordered_set/debug/merge4_neg.cc: New test.
* testsuite/util/testsuite_abi.h: [_GLIBCXX_DEBUG] Use normal unordered
container implementation.
Diffstat (limited to 'libstdc++-v3/include')
-rw-r--r-- | libstdc++-v3/include/bits/hashtable.h | 17 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/hashtable_policy.h | 6 | ||||
-rw-r--r-- | libstdc++-v3/include/debug/safe_container.h | 1 | ||||
-rw-r--r-- | libstdc++-v3/include/debug/safe_unordered_container.h | 90 | ||||
-rw-r--r-- | libstdc++-v3/include/debug/unordered_map | 113 | ||||
-rw-r--r-- | libstdc++-v3/include/debug/unordered_set | 112 |
6 files changed, 326 insertions, 13 deletions
diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h index 25c45d3..0e949d7 100644 --- a/libstdc++-v3/include/bits/hashtable.h +++ b/libstdc++-v3/include/bits/hashtable.h @@ -1065,14 +1065,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Merge from a compatible container into one with unique keys. template<typename _Compatible_Hashtable> void - _M_merge_unique(_Compatible_Hashtable& __src) noexcept + _M_merge_unique(_Compatible_Hashtable& __src) { static_assert(is_same_v<typename _Compatible_Hashtable::node_type, node_type>, "Node types are compatible"); __glibcxx_assert(get_allocator() == __src.get_allocator()); auto __n_elt = __src.size(); - for (auto __i = __src.begin(), __end = __src.end(); __i != __end;) + for (auto __i = __src.cbegin(), __end = __src.cend(); __i != __end;) { auto __pos = __i++; const key_type& __k = _ExtractKey{}(*__pos); @@ -1093,15 +1093,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Merge from a compatible container into one with equivalent keys. template<typename _Compatible_Hashtable> void - _M_merge_multi(_Compatible_Hashtable& __src) noexcept + _M_merge_multi(_Compatible_Hashtable& __src) { static_assert(is_same_v<typename _Compatible_Hashtable::node_type, node_type>, "Node types are compatible"); __glibcxx_assert(get_allocator() == __src.get_allocator()); this->reserve(size() + __src.size()); - for (auto __i = __src.begin(), __end = __src.end(); __i != __end;) - _M_reinsert_node_multi(cend(), __src.extract(__i++)); + for (auto __i = __src.cbegin(), __end = __src.cend(); __i != __end;) + { + auto __pos = __i++; + const key_type& __k = _ExtractKey{}(*__pos); + __hash_code __code = this->_M_hash_code(__k); + auto __nh = __src.extract(__pos); + _M_insert_multi_node(nullptr, __code, __nh._M_ptr); + __nh._M_ptr = nullptr; + } } #endif // C++17 diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h index 8c72043..c0295b7 100644 --- a/libstdc++-v3/include/bits/hashtable_policy.h +++ b/libstdc++-v3/include/bits/hashtable_policy.h @@ -60,19 +60,19 @@ namespace __detail // Helper function: return distance(first, last) for forward // iterators, or 0/1 for input iterators. - template<class _Iterator> + template<typename _Iterator> inline typename std::iterator_traits<_Iterator>::difference_type __distance_fw(_Iterator __first, _Iterator __last, std::input_iterator_tag) { return __first != __last ? 1 : 0; } - template<class _Iterator> + template<typename _Iterator> inline typename std::iterator_traits<_Iterator>::difference_type __distance_fw(_Iterator __first, _Iterator __last, std::forward_iterator_tag) { return std::distance(__first, __last); } - template<class _Iterator> + template<typename _Iterator> inline typename std::iterator_traits<_Iterator>::difference_type __distance_fw(_Iterator __first, _Iterator __last) { return __distance_fw(__first, __last, diff --git a/libstdc++-v3/include/debug/safe_container.h b/libstdc++-v3/include/debug/safe_container.h index 97c4716..5de55d6 100644 --- a/libstdc++-v3/include/debug/safe_container.h +++ b/libstdc++-v3/include/debug/safe_container.h @@ -78,7 +78,6 @@ namespace __gnu_debug { } #endif - public: // Copy assignment invalidate all iterators. _Safe_container& operator=(const _Safe_container&) _GLIBCXX_NOEXCEPT diff --git a/libstdc++-v3/include/debug/safe_unordered_container.h b/libstdc++-v3/include/debug/safe_unordered_container.h index aae1e2d..ce9d9ea 100644 --- a/libstdc++-v3/include/debug/safe_unordered_container.h +++ b/libstdc++-v3/include/debug/safe_unordered_container.h @@ -72,6 +72,96 @@ namespace __gnu_debug { return __it != __local_end; }); } +#if __cplusplus > 201402L + template<typename _ExtractKey, typename _Source> + struct _UContInvalidatePred + { + template<typename _Iterator> + bool + operator()(_Iterator __it) const + { return _M_source.count(_ExtractKey{}(*__it)) == 0; } + + const _Source& _M_source; + }; + + template<typename _ExtractKey, typename _Source> + struct _UMContInvalidatePred + { + template<typename _Iterator> + bool + operator()(_Iterator __it) const + { + auto __rng = + _M_source._M_base().equal_range(_ExtractKey{}(*__it)); + for (auto __rit = __rng.first; + __rit != __rng.second; ++__rit) + { + if (__it == __rit) + return false; + } + + return true; + } + + const _Source& _M_source; + }; + + template<typename _Source, typename _InvalidatePred> + struct _UContMergeGuard + { + _UContMergeGuard(_Source& __src) noexcept + : _M_source(__src), _M_size(__src.size()), _M_pred { __src } + { } + + _UContMergeGuard(const _UContMergeGuard&) = delete; + + ~_UContMergeGuard() + { + const std::size_t __size = _M_source.size(); + if (__size == _M_size) + return; + + __try + { + if (__size == 0) + _M_source._M_invalidate_all(); + else + { + _M_source._M_invalidate_if(_M_pred); + _M_source._M_invalidate_local_if(_M_pred); + } + } + __catch(...) + { + _M_source._M_invalidate_all(); + } + } + + _Source& _M_source; + const std::size_t _M_size; + _InvalidatePred _M_pred; + }; + + template<typename _ExtractKey, typename _Source> + static _UContMergeGuard<_Source, + _UContInvalidatePred<_ExtractKey, _Source>> + _S_uc_guard(_ExtractKey, _Source& __src) + { + typedef _UContInvalidatePred<_ExtractKey, _Source> _InvalidatePred; + return _UContMergeGuard<_Source, _InvalidatePred>(__src); + } + + template<typename _ExtractKey, typename _Source> + static _UContMergeGuard<_Source, + _UMContInvalidatePred<_ExtractKey, _Source>> + _S_umc_guard(_ExtractKey, _Source& __src) + { + typedef _UMContInvalidatePred<_ExtractKey, _Source> _InvalidatePred; + return _UContMergeGuard<_Source, _InvalidatePred>(__src); + } +#endif // C++17 + + public: void _M_invalidate_all() { diff --git a/libstdc++-v3/include/debug/unordered_map b/libstdc++-v3/include/debug/unordered_map index bb697d3..d6e184a 100644 --- a/libstdc++-v3/include/debug/unordered_map +++ b/libstdc++-v3/include/debug/unordered_map @@ -97,7 +97,12 @@ namespace __debug typedef typename _Base::key_type key_type; typedef typename _Base::value_type value_type; + typedef typename _Base::mapped_type mapped_type; + typedef typename _Base::pointer pointer; + typedef typename _Base::const_pointer const_pointer; + typedef typename _Base::reference reference; + typedef typename _Base::const_reference const_reference; typedef __gnu_debug::_Safe_iterator< _Base_iterator, unordered_map> iterator; typedef __gnu_debug::_Safe_iterator< @@ -106,6 +111,7 @@ namespace __debug _Base_local_iterator, unordered_map> local_iterator; typedef __gnu_debug::_Safe_local_iterator< _Base_const_local_iterator, unordered_map> const_local_iterator; + typedef typename _Base::difference_type difference_type; unordered_map() = default; @@ -209,6 +215,11 @@ namespace __debug return *this; } + using _Base::get_allocator; + using _Base::empty; + using _Base::size; + using _Base::max_size; + void swap(unordered_map& __x) noexcept( noexcept(declval<_Base&>().swap(__x)) ) @@ -291,6 +302,10 @@ namespace __debug return { _Base::cend(__b), this }; } + using _Base::bucket_count; + using _Base::max_bucket_count; + using _Base::bucket; + size_type bucket_size(size_type __b) const { @@ -298,6 +313,8 @@ namespace __debug return _Base::bucket_size(__b); } + using _Base::load_factor; + float max_load_factor() const noexcept { return _Base::max_load_factor(); } @@ -538,9 +555,38 @@ namespace __debug return { _Base::insert(__hint.base(), std::move(__nh)), this }; } - using _Base::merge; + template<typename _H2, typename _P2> + void + merge(unordered_map<_Key, _Tp, _H2, _P2, _Alloc>& __source) + { + auto __guard + = _Safe::_S_uc_guard(std::__detail::_Select1st{}, __source); + _Base::merge(__source._M_base()); + } + + template<typename _H2, typename _P2> + void + merge(unordered_map<_Key, _Tp, _H2, _P2, _Alloc>&& __source) + { merge(__source); } + + template<typename _H2, typename _P2> + void + merge(unordered_multimap<_Key, _Tp, _H2, _P2, _Alloc>& __source) + { + auto __guard + = _Safe::_S_umc_guard(std::__detail::_Select1st{}, __source); + _Base::merge(__source._M_base()); + } + + template<typename _H2, typename _P2> + void + merge(unordered_multimap<_Key, _Tp, _H2, _P2, _Alloc>&& __source) + { merge(__source); } #endif // C++17 + using _Base::hash_function; + using _Base::key_eq; + iterator find(const key_type& __key) { return { _Base::find(__key), this }; } @@ -567,6 +613,11 @@ namespace __debug { return { _Base::find(__k), this }; } #endif + using _Base::count; +#if __cplusplus > 201703L + using _Base::contains; +#endif + std::pair<iterator, iterator> equal_range(const key_type& __key) { @@ -605,6 +656,9 @@ namespace __debug } #endif + using _Base::operator[]; + using _Base::at; + size_type erase(const key_type& __key) { @@ -651,6 +705,9 @@ namespace __debug return { __next, this }; } + using _Base::rehash; + using _Base::reserve; + _Base& _M_base() noexcept { return *this; } @@ -843,7 +900,12 @@ namespace __debug typedef typename _Base::key_type key_type; typedef typename _Base::value_type value_type; + typedef typename _Base::mapped_type mapped_type; + typedef typename _Base::pointer pointer; + typedef typename _Base::const_pointer const_pointer; + typedef typename _Base::reference reference; + typedef typename _Base::const_reference const_reference; typedef __gnu_debug::_Safe_iterator< _Base_iterator, unordered_multimap> iterator; typedef __gnu_debug::_Safe_iterator< @@ -852,6 +914,7 @@ namespace __debug _Base_local_iterator, unordered_multimap> local_iterator; typedef __gnu_debug::_Safe_local_iterator< _Base_const_local_iterator, unordered_multimap> const_local_iterator; + typedef typename _Base::difference_type difference_type; unordered_multimap() = default; @@ -952,6 +1015,11 @@ namespace __debug return *this; } + using _Base::get_allocator; + using _Base::empty; + using _Base::size; + using _Base::max_size; + void swap(unordered_multimap& __x) noexcept( noexcept(declval<_Base&>().swap(__x)) ) @@ -1034,6 +1102,10 @@ namespace __debug return { _Base::cend(__b), this }; } + using _Base::bucket_count; + using _Base::max_bucket_count; + using _Base::bucket; + size_type bucket_size(size_type __b) const { @@ -1192,9 +1264,38 @@ namespace __debug return { _Base::insert(__hint.base(), std::move(__nh)), this }; } - using _Base::merge; + template<typename _H2, typename _P2> + void + merge(unordered_multimap<_Key, _Tp, _H2, _P2, _Alloc>& __source) + { + auto __guard + = _Safe::_S_umc_guard(std::__detail::_Select1st{}, __source); + _Base::merge(__source._M_base()); + } + + template<typename _H2, typename _P2> + void + merge(unordered_multimap<_Key, _Tp, _H2, _P2, _Alloc>&& __source) + { merge(__source); } + + template<typename _H2, typename _P2> + void + merge(unordered_map<_Key, _Tp, _H2, _P2, _Alloc>& __source) + { + auto __guard + = _Safe::_S_uc_guard(std::__detail::_Select1st{}, __source); + _Base::merge(__source._M_base()); + } + + template<typename _H2, typename _P2> + void + merge(unordered_map<_Key, _Tp, _H2, _P2, _Alloc>&& __source) + { merge(__source); } #endif // C++17 + using _Base::hash_function; + using _Base::key_eq; + iterator find(const key_type& __key) { return { _Base::find(__key), this }; } @@ -1221,6 +1322,11 @@ namespace __debug { return { _Base::find(__k), this }; } #endif + using _Base::count; +#if __cplusplus > 201703L + using _Base::contains; +#endif + std::pair<iterator, iterator> equal_range(const key_type& __key) { @@ -1309,6 +1415,9 @@ namespace __debug return { __next, this }; } + using _Base::rehash; + using _Base::reserve; + _Base& _M_base() noexcept { return *this; } diff --git a/libstdc++-v3/include/debug/unordered_set b/libstdc++-v3/include/debug/unordered_set index c259109..7dc91fa 100644 --- a/libstdc++-v3/include/debug/unordered_set +++ b/libstdc++-v3/include/debug/unordered_set @@ -88,6 +88,7 @@ namespace __debug public: typedef typename _Base::size_type size_type; + typedef typename _Base::difference_type difference_type; typedef typename _Base::hasher hasher; typedef typename _Base::key_equal key_equal; typedef typename _Base::allocator_type allocator_type; @@ -95,6 +96,10 @@ namespace __debug typedef typename _Base::key_type key_type; typedef typename _Base::value_type value_type; + typedef typename _Base::pointer pointer; + typedef typename _Base::const_pointer const_pointer; + typedef typename _Base::reference reference; + typedef typename _Base::const_reference const_reference; typedef __gnu_debug::_Safe_iterator< _Base_iterator, unordered_set> iterator; typedef __gnu_debug::_Safe_iterator< @@ -203,6 +208,11 @@ namespace __debug return *this; } + using _Base::get_allocator; + using _Base::empty; + using _Base::size; + using _Base::max_size; + void swap(unordered_set& __x) noexcept( noexcept(declval<_Base&>().swap(__x)) ) @@ -285,6 +295,9 @@ namespace __debug return { _Base::cend(__b), this }; } + using _Base::bucket_count; + using _Base::max_bucket_count; + size_type bucket_size(size_type __b) const { @@ -292,6 +305,9 @@ namespace __debug return _Base::bucket_size(__b); } + using _Base::bucket; + using _Base::load_factor; + float max_load_factor() const noexcept { return _Base::max_load_factor(); } @@ -303,6 +319,9 @@ namespace __debug _Base::max_load_factor(__f); } + using _Base::rehash; + using _Base::reserve; + template<typename... _Args> std::pair<iterator, bool> emplace(_Args&&... __args) @@ -423,9 +442,38 @@ namespace __debug return { _Base::insert(__hint.base(), std::move(__nh)), this }; } - using _Base::merge; + template<typename _H2, typename _P2> + void + merge(unordered_set<_Value, _H2, _P2, _Alloc>& __source) + { + auto __guard + = _Safe::_S_uc_guard(std::__detail::_Identity{}, __source); + _Base::merge(__source._M_base()); + } + + template<typename _H2, typename _P2> + void + merge(unordered_set<_Value, _H2, _P2, _Alloc>&& __source) + { merge(__source); } + + template<typename _H2, typename _P2> + void + merge(unordered_multiset<_Value, _H2, _P2, _Alloc>& __source) + { + auto __guard + = _Safe::_S_umc_guard(std::__detail::_Identity{}, __source); + _Base::merge(__source._M_base()); + } + + template<typename _H2, typename _P2> + void + merge(unordered_multiset<_Value, _H2, _P2, _Alloc>&& __source) + { merge(__source); } #endif // C++17 + using _Base::hash_function; + using _Base::key_eq; + iterator find(const key_type& __key) { return { _Base::find(__key), this }; } @@ -452,6 +500,12 @@ namespace __debug { return { _Base::find(__k), this }; } #endif + using _Base::count; + +#if __cplusplus > 201703L + using _Base::contains; +#endif + std::pair<iterator, iterator> equal_range(const key_type& __key) { @@ -707,6 +761,7 @@ namespace __debug public: typedef typename _Base::size_type size_type; + typedef typename _Base::difference_type difference_type; typedef typename _Base::hasher hasher; typedef typename _Base::key_equal key_equal; typedef typename _Base::allocator_type allocator_type; @@ -714,6 +769,10 @@ namespace __debug typedef typename _Base::key_type key_type; typedef typename _Base::value_type value_type; + typedef typename _Base::pointer pointer; + typedef typename _Base::const_pointer const_pointer; + typedef typename _Base::reference reference; + typedef typename _Base::const_reference const_reference; typedef __gnu_debug::_Safe_iterator< _Base_iterator, unordered_multiset> iterator; typedef __gnu_debug::_Safe_iterator< @@ -822,6 +881,11 @@ namespace __debug return *this; } + using _Base::get_allocator; + using _Base::empty; + using _Base::size; + using _Base::max_size; + void swap(unordered_multiset& __x) noexcept( noexcept(declval<_Base&>().swap(__x)) ) @@ -904,6 +968,9 @@ namespace __debug return { _Base::cend(__b), this }; } + using _Base::bucket_count; + using _Base::max_bucket_count; + size_type bucket_size(size_type __b) const { @@ -911,6 +978,9 @@ namespace __debug return _Base::bucket_size(__b); } + using _Base::bucket; + using _Base::load_factor; + float max_load_factor() const noexcept { return _Base::max_load_factor(); } @@ -922,6 +992,9 @@ namespace __debug _Base::max_load_factor(__f); } + using _Base::rehash; + using _Base::reserve; + template<typename... _Args> iterator emplace(_Args&&... __args) @@ -1037,9 +1110,38 @@ namespace __debug return { _Base::insert(__hint.base(), std::move(__nh)), this }; } - using _Base::merge; + template<typename _H2, typename _P2> + void + merge(unordered_multiset<_Value, _H2, _P2, _Alloc>& __source) + { + auto __guard + = _Safe::_S_umc_guard(std::__detail::_Identity{}, __source); + _Base::merge(__source._M_base()); + } + + template<typename _H2, typename _P2> + void + merge(unordered_multiset<_Value, _H2, _P2, _Alloc>&& __source) + { merge(__source); } + + template<typename _H2, typename _P2> + void + merge(unordered_set<_Value, _H2, _P2, _Alloc>& __source) + { + auto __guard + = _Safe::_S_uc_guard(std::__detail::_Identity{}, __source); + _Base::merge(__source._M_base()); + } + + template<typename _H2, typename _P2> + void + merge(unordered_set<_Value, _H2, _P2, _Alloc>&& __source) + { merge(__source); } #endif // C++17 + using _Base::hash_function; + using _Base::key_eq; + iterator find(const key_type& __key) { return { _Base::find(__key), this }; } @@ -1066,6 +1168,12 @@ namespace __debug { return { _Base::find(__k), this }; } #endif + using _Base::count; + +#if __cplusplus > 201703L + using _Base::contains; +#endif + std::pair<iterator, iterator> equal_range(const key_type& __key) { |