mirror of
https://github.com/gcc-mirror/gcc.git
synced 2024-11-21 13:40:47 +00:00
libstdc++: Refactor Hashtable insertion [PR115285]
This completely reworks the internal member functions for insertion into unordered containers. Currently we use a mixture of tag dispatching (for unique vs non-unique keys) and template specialization (for maps vs sets) to correctly implement insert and emplace members. This removes a lot of complexity and indirection by using 'if constexpr' to select the appropriate member function to call. Previously there were four overloads of _M_emplace, for unique keys and non-unique keys, and for hinted insertion and non-hinted. However two of those were redundant, because we always ignore the hint for unique keys and always use a hint for non-unique keys. Those four overloads have been replaced by two new non-overloaded function templates: _M_emplace_uniq and _M_emplace_multi. The former is for unique keys and doesn't take a hint, and the latter is for non-unique keys and takes a hint. In the body of _M_emplace_uniq there are special cases to handle emplacing values from which a key_type can be extracted directly. This means we don't need to allocate a node and construct a value_type that might be discarded if an equivalent key is already present. The special case applies when emplacing the key_type into std::unordered_set, or when emplacing std::pair<cv key_type, X> into std::unordered_map, or when emplacing two values into std::unordered_map where the first has type cv key_type. For the std::unordered_set case, obviously if we're inserting something that's already the key_type, we can look it up directly. For the std::unordered_map cases, we know that the inserted std::pair<const key_type, mapped_type> would have its first element initialized from first member of a std::pair value, or from the first of two values, so if that is a key_type, we can look that up directly. All the _M_insert overloads used a node generator parameter, but apart from the one case where _M_insert_range was called from _Hashtable::operator=(initializer_list<value_type>), that parameter was always the _AllocNode type, never the _ReuseOrAllocNode type. Because operator=(initializer_list<value_type>) was rewritten in an earlier commit, all calls to _M_insert now use _AllocNode, so there's no reason to pass the generator as a template parameter when inserting. The multiple overloads of _Hashtable::_M_insert can all be removed now, because the _Insert_base::insert members now call either _M_emplace_uniq or _M_emplace_multi directly, only passing a hint to the latter. Which one to call is decided using 'if constexpr (__unique_keys::value)' so there is no unnecessary code instantiation, and overload resolution is much simpler. The partial specializations of the _Insert class template can be entirely removed, moving the minor differences in 'insert' member functions into the common _Insert_base base class. The different behaviour for maps and sets can be implemented using enable_if constraints and 'if constexpr'. With the _Insert class template no longer needed, the _Insert_base class template can be renamed to _Insert. This is a minor simplification for the complex inheritance hierarchy used by _Hashtable, removing one base class. It also means one less class template instantiation, and no need to match the right partial specialization of _Insert. The _Insert base class could be removed entirely by moving all its 'insert' members into _Hashtable, because without any variation in specializations of _Insert there is no reason to use a base class to define those members. That is left for a later commit. Consistently using _M_emplace_uniq or _M_emplace_multi for insertion means we no longer attempt to avoid constructing a value_type object to find its key, removing the PR libstdc++/96088 optimizations. This fixes the bugs caused by those optimizations, such as PR libstdc++/115285, but causes regressions in the expected number of allocations and temporary objects constructed for the PR 96088 tests. It should be noted that the "regressions" in the 96088 tests put us exactly level with the number of allocations done by libc++ for those same tests. To mitigate this to some extent, _M_emplace_uniq detects when the emplace arguments already contain a key_type (either as the sole argument, for unordered_set, or as the first part of a pair of arguments, for unordered_map). In that specific case we don't need to allocate a node and construct a value type to check for an existing element with equivalent key. The remaining regressions in the number of allocations and temporaries should be addressed separately, with more conservative optimizations specific to std::string. That is not part of this commit. libstdc++-v3/ChangeLog: PR libstdc++/115285 * include/bits/hashtable.h (_Hashtable::_M_emplace): Replace with _M_emplace_uniq and _M_emplace_multi. (_Hashtable::_S_forward_key, _Hashtable::_M_insert_unique) (_Hashtable::_M_insert_unique_aux, _Hashtable::_M_insert): Remove. * include/bits/hashtable_policy.h (_ConvertToValueType): Remove. (_Insert_base::_M_insert_range): Remove overload for unique keys and rename overload for non-unique keys to ... (_Insert_base::_M_insert_range_multi): ... this. (_Insert_base::insert): Call _M_emplace_uniq or _M_emplace_multi instead of _M_insert. Add insert overloads from _Insert. (_Insert_base): Rename to _Insert. (_Insert): Remove * testsuite/23_containers/unordered_map/96088.cc: Adjust expected number of allocations. * testsuite/23_containers/unordered_set/96088.cc: Likewise.
This commit is contained in:
parent
afc9351ebb
commit
ce2cf1f032
@ -371,13 +371,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
typename _ExtractKeya, typename _Equala,
|
||||
typename _Hasha, typename _RangeHasha, typename _Unuseda,
|
||||
typename _RehashPolicya, typename _Traitsa>
|
||||
friend struct __detail::_Insert_base;
|
||||
|
||||
template<typename _Keya, typename _Valuea, typename _Alloca,
|
||||
typename _ExtractKeya, typename _Equala,
|
||||
typename _Hasha, typename _RangeHasha, typename _Unuseda,
|
||||
typename _RehashPolicya, typename _Traitsa,
|
||||
bool _Constant_iteratorsa>
|
||||
friend struct __detail::_Insert;
|
||||
|
||||
template<typename _Keya, typename _Valuea, typename _Alloca,
|
||||
@ -940,77 +933,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
|
||||
template<typename... _Args>
|
||||
std::pair<iterator, bool>
|
||||
_M_emplace(true_type __uks, _Args&&... __args);
|
||||
_M_emplace_uniq(_Args&&... __args);
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wc++14-extensions" // variable templates
|
||||
template<typename _Arg, typename _DArg = __remove_cvref_t<_Arg>,
|
||||
typename = _ExtractKey>
|
||||
static constexpr bool __is_key_type = false;
|
||||
|
||||
template<typename _Arg>
|
||||
static constexpr bool
|
||||
__is_key_type<_Arg, key_type, __detail::_Identity> = true;
|
||||
|
||||
template<typename _Arg, typename _Arg1, typename _Arg2>
|
||||
static constexpr bool
|
||||
__is_key_type<_Arg, pair<_Arg1, _Arg2>, __detail::_Select1st>
|
||||
= is_same<__remove_cvref_t<_Arg1>, key_type>::value;
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
template<typename... _Args>
|
||||
iterator
|
||||
_M_emplace(false_type __uks, _Args&&... __args)
|
||||
{ return _M_emplace(cend(), __uks, std::forward<_Args>(__args)...); }
|
||||
|
||||
// Emplace with hint, useless when keys are unique.
|
||||
template<typename... _Args>
|
||||
iterator
|
||||
_M_emplace(const_iterator, true_type __uks, _Args&&... __args)
|
||||
{ return _M_emplace(__uks, std::forward<_Args>(__args)...).first; }
|
||||
|
||||
template<typename... _Args>
|
||||
iterator
|
||||
_M_emplace(const_iterator, false_type __uks, _Args&&... __args);
|
||||
|
||||
template<typename _Kt, typename _Arg, typename _NodeGenerator>
|
||||
std::pair<iterator, bool>
|
||||
_M_insert_unique(_Kt&&, _Arg&&, _NodeGenerator&);
|
||||
|
||||
template<typename _Arg, typename _NodeGenerator>
|
||||
std::pair<iterator, bool>
|
||||
_M_insert_unique_aux(_Arg&& __arg, _NodeGenerator& __node_gen)
|
||||
{
|
||||
using _Kt = decltype(_ExtractKey{}(std::forward<_Arg>(__arg)));
|
||||
constexpr bool __is_key_type
|
||||
= is_same<__remove_cvref_t<_Kt>, key_type>::value;
|
||||
using _Fwd_key = __conditional_t<__is_key_type, _Kt&&, key_type>;
|
||||
return _M_insert_unique(
|
||||
static_cast<_Fwd_key>(_ExtractKey{}(std::forward<_Arg>(__arg))),
|
||||
std::forward<_Arg>(__arg), __node_gen);
|
||||
}
|
||||
|
||||
template<typename _Arg, typename _NodeGenerator>
|
||||
std::pair<iterator, bool>
|
||||
_M_insert(_Arg&& __arg, _NodeGenerator& __node_gen,
|
||||
true_type /* __uks */)
|
||||
{
|
||||
using __detail::_Identity;
|
||||
using _Vt = __conditional_t<is_same<_ExtractKey, _Identity>::value
|
||||
|| __is_pair<__remove_cvref_t<_Arg>>,
|
||||
_Arg&&, value_type>;
|
||||
return _M_insert_unique_aux(
|
||||
static_cast<_Vt>(std::forward<_Arg>(__arg)), __node_gen);
|
||||
}
|
||||
|
||||
template<typename _Arg, typename _NodeGenerator>
|
||||
iterator
|
||||
_M_insert(_Arg&& __arg, _NodeGenerator& __node_gen,
|
||||
false_type __uks)
|
||||
{
|
||||
return _M_insert(cend(), std::forward<_Arg>(__arg),
|
||||
__node_gen, __uks);
|
||||
}
|
||||
|
||||
// Insert with hint, not used when keys are unique.
|
||||
template<typename _Arg, typename _NodeGenerator>
|
||||
iterator
|
||||
_M_insert(const_iterator, _Arg&& __arg,
|
||||
_NodeGenerator& __node_gen, true_type __uks)
|
||||
{
|
||||
return
|
||||
_M_insert(std::forward<_Arg>(__arg), __node_gen, __uks).first;
|
||||
}
|
||||
|
||||
// Insert with hint when keys are not unique.
|
||||
template<typename _Arg, typename _NodeGenerator>
|
||||
iterator
|
||||
_M_insert(const_iterator, _Arg&&,
|
||||
_NodeGenerator&, false_type __uks);
|
||||
_M_emplace_multi(const_iterator, _Args&&... __args);
|
||||
|
||||
size_type
|
||||
_M_erase(true_type __uks, const key_type&);
|
||||
@ -1022,19 +965,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
_M_erase(size_type __bkt, __node_base_ptr __prev_n, __node_ptr __n);
|
||||
|
||||
public:
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
|
||||
// Emplace
|
||||
template<typename... _Args>
|
||||
__ireturn_type
|
||||
emplace(_Args&&... __args)
|
||||
{ return _M_emplace(__unique_keys{}, std::forward<_Args>(__args)...); }
|
||||
{
|
||||
if constexpr (__unique_keys::value)
|
||||
return _M_emplace_uniq(std::forward<_Args>(__args)...);
|
||||
else
|
||||
return _M_emplace_multi(cend(), std::forward<_Args>(__args)...);
|
||||
}
|
||||
|
||||
template<typename... _Args>
|
||||
iterator
|
||||
emplace_hint(const_iterator __hint, _Args&&... __args)
|
||||
{
|
||||
return _M_emplace(__hint, __unique_keys{},
|
||||
std::forward<_Args>(__args)...);
|
||||
if constexpr (__unique_keys::value)
|
||||
return _M_emplace_uniq(std::forward<_Args>(__args)...).first;
|
||||
else
|
||||
return _M_emplace_multi(__hint, std::forward<_Args>(__args)...);
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// Insert member functions via inheritance.
|
||||
|
||||
@ -1328,9 +1281,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
_M_bucket_count = __bkt_count;
|
||||
}
|
||||
|
||||
__alloc_node_gen_t __node_gen(*this);
|
||||
for (; __f != __l; ++__f)
|
||||
_M_insert(*__f, __node_gen, __uks);
|
||||
_M_emplace_multi(cend(), *__f);
|
||||
}
|
||||
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
@ -2160,6 +2112,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
return __prev_n;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
typename _Hash, typename _RangeHash, typename _Unused,
|
||||
@ -2168,33 +2122,69 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
auto
|
||||
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
|
||||
_M_emplace(true_type /* __uks */, _Args&&... __args)
|
||||
_M_emplace_uniq(_Args&&... __args)
|
||||
-> pair<iterator, bool>
|
||||
{
|
||||
// First build the node to get access to the hash code
|
||||
_Scoped_node __node { this, std::forward<_Args>(__args)... };
|
||||
const key_type& __k = _ExtractKey{}(__node._M_node->_M_v());
|
||||
const key_type* __kp = nullptr;
|
||||
|
||||
if constexpr (sizeof...(_Args) == 1)
|
||||
{
|
||||
if constexpr (__is_key_type<_Args...>)
|
||||
{
|
||||
const auto& __key = _ExtractKey{}(__args...);
|
||||
__kp = std::__addressof(__key);
|
||||
}
|
||||
}
|
||||
else if constexpr (sizeof...(_Args) == 2)
|
||||
{
|
||||
pair<const _Args&...> __refs(__args...);
|
||||
if constexpr (__is_key_type<pair<_Args...>>)
|
||||
{
|
||||
const auto& __key = _ExtractKey{}(__refs);
|
||||
__kp = std::__addressof(__key);
|
||||
}
|
||||
}
|
||||
|
||||
_Scoped_node __node { __node_ptr(), this }; // Do not create node yet.
|
||||
__hash_code __code = 0;
|
||||
size_type __bkt = 0;
|
||||
|
||||
if (__kp == nullptr)
|
||||
{
|
||||
// Didn't extract a key from the args, so build the node.
|
||||
__node._M_node
|
||||
= this->_M_allocate_node(std::forward<_Args>(__args)...);
|
||||
const key_type& __key = _ExtractKey{}(__node._M_node->_M_v());
|
||||
__kp = std::__addressof(__key);
|
||||
}
|
||||
|
||||
const size_type __size = size();
|
||||
if (__size <= __small_size_threshold())
|
||||
{
|
||||
for (auto __it = _M_begin(); __it; __it = __it->_M_next())
|
||||
if (this->_M_key_equals(__k, *__it))
|
||||
// There is already an equivalent node, no insertion
|
||||
if (this->_M_key_equals(*__kp, *__it))
|
||||
// There is already an equivalent node, no insertion.
|
||||
return { iterator(__it), false };
|
||||
}
|
||||
|
||||
__hash_code __code = this->_M_hash_code(__k);
|
||||
size_type __bkt = _M_bucket_index(__code);
|
||||
__code = this->_M_hash_code(*__kp);
|
||||
__bkt = _M_bucket_index(__code);
|
||||
|
||||
if (__size > __small_size_threshold())
|
||||
if (__node_ptr __p = _M_find_node(__bkt, __k, __code))
|
||||
// There is already an equivalent node, no insertion
|
||||
if (__node_ptr __p = _M_find_node(__bkt, *__kp, __code))
|
||||
// There is already an equivalent node, no insertion.
|
||||
return { iterator(__p), false };
|
||||
|
||||
if (!__node._M_node)
|
||||
__node._M_node
|
||||
= this->_M_allocate_node(std::forward<_Args>(__args)...);
|
||||
|
||||
// Insert the node
|
||||
auto __pos = _M_insert_unique_node(__bkt, __code, __node._M_node);
|
||||
__node._M_node = nullptr;
|
||||
return { __pos, true };
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
@ -2204,12 +2194,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
auto
|
||||
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
|
||||
_M_emplace(const_iterator __hint, false_type /* __uks */,
|
||||
_Args&&... __args)
|
||||
_M_emplace_multi(const_iterator __hint, _Args&&... __args)
|
||||
-> iterator
|
||||
{
|
||||
// First build the node to get its hash code.
|
||||
_Scoped_node __node { this, std::forward<_Args>(__args)... };
|
||||
_Scoped_node __node { this, std::forward<_Args>(__args)... };
|
||||
const key_type& __k = _ExtractKey{}(__node._M_node->_M_v());
|
||||
|
||||
auto __res = this->_M_compute_hash_code(__hint._M_cur, __k);
|
||||
@ -2335,71 +2324,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
return iterator(__node);
|
||||
}
|
||||
|
||||
// Insert v if no element with its key is already present.
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
typename _Hash, typename _RangeHash, typename _Unused,
|
||||
typename _RehashPolicy, typename _Traits>
|
||||
template<typename _Kt, typename _Arg, typename _NodeGenerator>
|
||||
auto
|
||||
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
|
||||
_M_insert_unique(_Kt&& __k, _Arg&& __v,
|
||||
_NodeGenerator& __node_gen)
|
||||
-> pair<iterator, bool>
|
||||
{
|
||||
const size_type __size = size();
|
||||
if (__size <= __small_size_threshold())
|
||||
for (auto __it = _M_begin(); __it; __it = __it->_M_next())
|
||||
if (this->_M_key_equals_tr(__k, *__it))
|
||||
return { iterator(__it), false };
|
||||
|
||||
__hash_code __code = this->_M_hash_code_tr(__k);
|
||||
size_type __bkt = _M_bucket_index(__code);
|
||||
|
||||
if (__size > __small_size_threshold())
|
||||
if (__node_ptr __node = _M_find_node_tr(__bkt, __k, __code))
|
||||
return { iterator(__node), false };
|
||||
|
||||
_Scoped_node __node {
|
||||
__node_builder_t::_S_build(std::forward<_Kt>(__k),
|
||||
std::forward<_Arg>(__v),
|
||||
__node_gen),
|
||||
this
|
||||
};
|
||||
auto __pos
|
||||
= _M_insert_unique_node(__bkt, __code, __node._M_node);
|
||||
__node._M_node = nullptr;
|
||||
return { __pos, true };
|
||||
}
|
||||
|
||||
// Insert v unconditionally.
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
typename _Hash, typename _RangeHash, typename _Unused,
|
||||
typename _RehashPolicy, typename _Traits>
|
||||
template<typename _Arg, typename _NodeGenerator>
|
||||
auto
|
||||
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
|
||||
_M_insert(const_iterator __hint, _Arg&& __v,
|
||||
_NodeGenerator& __node_gen,
|
||||
false_type /* __uks */)
|
||||
-> iterator
|
||||
{
|
||||
// First allocate new node so that we don't do anything if it throws.
|
||||
_Scoped_node __node{ __node_gen(std::forward<_Arg>(__v)), this };
|
||||
|
||||
// Second compute the hash code so that we don't rehash if it throws.
|
||||
auto __res = this->_M_compute_hash_code(
|
||||
__hint._M_cur, _ExtractKey{}(__node._M_node->_M_v()));
|
||||
|
||||
auto __pos
|
||||
= _M_insert_multi_node(__res.first, __res.second, __node._M_node);
|
||||
__node._M_node = nullptr;
|
||||
return __pos;
|
||||
}
|
||||
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
typename _Hash, typename _RangeHash, typename _Unused,
|
||||
|
@ -947,16 +947,15 @@ namespace __detail
|
||||
_RangeHash, _Unused, _RehashPolicy, _Traits, __uniq>
|
||||
{ };
|
||||
|
||||
/**
|
||||
* Primary class template _Insert_base.
|
||||
*
|
||||
* Defines @c insert member functions appropriate to all _Hashtables.
|
||||
*/
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
|
||||
|
||||
// Defines `insert` member functions for _Hashtables.
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
typename _Hash, typename _RangeHash, typename _Unused,
|
||||
typename _RehashPolicy, typename _Traits>
|
||||
struct _Insert_base
|
||||
struct _Insert
|
||||
{
|
||||
protected:
|
||||
using __hashtable_base = _Hashtable_base<_Key, _Value, _ExtractKey,
|
||||
@ -981,19 +980,14 @@ namespace __detail
|
||||
using __node_alloc_type = typename __hashtable_alloc::__node_alloc_type;
|
||||
using __node_gen_type = _AllocNode<__node_alloc_type>;
|
||||
|
||||
[[__gnu__::__always_inline__]]
|
||||
__hashtable&
|
||||
_M_conjure_hashtable()
|
||||
{ return *(static_cast<__hashtable*>(this)); }
|
||||
|
||||
template<typename _InputIterator, typename _NodeGetter>
|
||||
template<typename _InputIterator>
|
||||
void
|
||||
_M_insert_range(_InputIterator __first, _InputIterator __last,
|
||||
_NodeGetter&, true_type __uks);
|
||||
|
||||
template<typename _InputIterator, typename _NodeGetter>
|
||||
void
|
||||
_M_insert_range(_InputIterator __first, _InputIterator __last,
|
||||
_NodeGetter&, false_type __uks);
|
||||
_M_insert_range_multi(_InputIterator __first, _InputIterator __last);
|
||||
|
||||
public:
|
||||
using iterator = _Node_iterator<_Value, __constant_iterators::value,
|
||||
@ -1011,16 +1005,40 @@ namespace __detail
|
||||
insert(const value_type& __v)
|
||||
{
|
||||
__hashtable& __h = _M_conjure_hashtable();
|
||||
__node_gen_type __node_gen(__h);
|
||||
return __h._M_insert(__v, __node_gen, __unique_keys{});
|
||||
if constexpr (__unique_keys::value)
|
||||
return __h._M_emplace_uniq(__v);
|
||||
else
|
||||
return __h._M_emplace_multi(__h.cend(), __v);
|
||||
}
|
||||
|
||||
iterator
|
||||
insert(const_iterator __hint, const value_type& __v)
|
||||
{
|
||||
__hashtable& __h = _M_conjure_hashtable();
|
||||
__node_gen_type __node_gen(__h);
|
||||
return __h._M_insert(__hint, __v, __node_gen, __unique_keys{});
|
||||
if constexpr (__unique_keys::value)
|
||||
return __h._M_emplace_uniq(__v).first;
|
||||
else
|
||||
return __h._M_emplace_multi(__hint, __v);
|
||||
}
|
||||
|
||||
__ireturn_type
|
||||
insert(value_type&& __v)
|
||||
{
|
||||
__hashtable& __h = _M_conjure_hashtable();
|
||||
if constexpr (__unique_keys::value)
|
||||
return __h._M_emplace_uniq(std::move(__v));
|
||||
else
|
||||
return __h._M_emplace_multi(__h.cend(), std::move(__v));
|
||||
}
|
||||
|
||||
iterator
|
||||
insert(const_iterator __hint, value_type&& __v)
|
||||
{
|
||||
__hashtable& __h = _M_conjure_hashtable();
|
||||
if constexpr (__unique_keys::value)
|
||||
return __h._M_emplace_uniq(std::move(__v)).first;
|
||||
else
|
||||
return __h._M_emplace_multi(__hint, std::move(__v));
|
||||
}
|
||||
|
||||
#ifdef __glibcxx_unordered_map_try_emplace // C++ >= 17 && HOSTED
|
||||
@ -1056,39 +1074,52 @@ namespace __detail
|
||||
insert(_InputIterator __first, _InputIterator __last)
|
||||
{
|
||||
__hashtable& __h = _M_conjure_hashtable();
|
||||
__node_gen_type __node_gen(__h);
|
||||
return _M_insert_range(__first, __last, __node_gen, __unique_keys{});
|
||||
if constexpr (__unique_keys::value)
|
||||
for (; __first != __last; ++__first)
|
||||
__h._M_emplace_uniq(*__first);
|
||||
else
|
||||
return _M_insert_range_multi(__first, __last);
|
||||
}
|
||||
|
||||
// This overload is only defined for maps, not sets.
|
||||
template<typename _Pair,
|
||||
typename = _Require<__not_<is_same<_Key, _Value>>,
|
||||
is_constructible<value_type, _Pair&&>>>
|
||||
__ireturn_type
|
||||
insert(_Pair&& __v)
|
||||
{
|
||||
__hashtable& __h = this->_M_conjure_hashtable();
|
||||
if constexpr (__unique_keys::value)
|
||||
return __h._M_emplace_uniq(std::forward<_Pair>(__v));
|
||||
else
|
||||
return __h._M_emplace_multi(__h.cend(), std::forward<_Pair>(__v));
|
||||
}
|
||||
|
||||
// This overload is only defined for maps, not sets.
|
||||
template<typename _Pair,
|
||||
typename = _Require<__not_<is_same<_Key, _Value>>,
|
||||
is_constructible<value_type, _Pair&&>>>
|
||||
iterator
|
||||
insert(const_iterator __hint, _Pair&& __v)
|
||||
{
|
||||
__hashtable& __h = this->_M_conjure_hashtable();
|
||||
if constexpr (__unique_keys::value)
|
||||
return __h._M_emplace_uniq(std::forward<_Pair>(__v));
|
||||
else
|
||||
return __h._M_emplace_multi(__hint, std::forward<_Pair>(__v));
|
||||
}
|
||||
};
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
typename _Hash, typename _RangeHash, typename _Unused,
|
||||
typename _RehashPolicy, typename _Traits>
|
||||
template<typename _InputIterator, typename _NodeGetter>
|
||||
template<typename _InputIterator>
|
||||
void
|
||||
_Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_Hash, _RangeHash, _Unused,
|
||||
_RehashPolicy, _Traits>::
|
||||
_M_insert_range(_InputIterator __first, _InputIterator __last,
|
||||
_NodeGetter& __node_gen, true_type __uks)
|
||||
{
|
||||
__hashtable& __h = _M_conjure_hashtable();
|
||||
for (; __first != __last; ++__first)
|
||||
__h._M_insert(*__first, __node_gen, __uks);
|
||||
}
|
||||
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
typename _Hash, typename _RangeHash, typename _Unused,
|
||||
typename _RehashPolicy, typename _Traits>
|
||||
template<typename _InputIterator, typename _NodeGetter>
|
||||
void
|
||||
_Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_Hash, _RangeHash, _Unused,
|
||||
_RehashPolicy, _Traits>::
|
||||
_M_insert_range(_InputIterator __first, _InputIterator __last,
|
||||
_NodeGetter& __node_gen, false_type __uks)
|
||||
_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash,
|
||||
_Unused, _RehashPolicy, _Traits>::
|
||||
_M_insert_range_multi(_InputIterator __first, _InputIterator __last)
|
||||
{
|
||||
using __rehash_guard_t = typename __hashtable::__rehash_guard_t;
|
||||
using __pair_type = std::pair<bool, std::size_t>;
|
||||
@ -1105,120 +1136,13 @@ namespace __detail
|
||||
__n_elt);
|
||||
|
||||
if (__do_rehash.first)
|
||||
__h._M_rehash(__do_rehash.second, __uks);
|
||||
__h._M_rehash(__do_rehash.second, false_type{});
|
||||
|
||||
__rehash_guard._M_guarded_obj = nullptr;
|
||||
for (; __first != __last; ++__first)
|
||||
__h._M_insert(*__first, __node_gen, __uks);
|
||||
__h._M_emplace_multi(__h.cend(), *__first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary class template _Insert.
|
||||
*
|
||||
* Defines @c insert member functions that depend on _Hashtable policies,
|
||||
* via partial specializations.
|
||||
*/
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
typename _Hash, typename _RangeHash, typename _Unused,
|
||||
typename _RehashPolicy, typename _Traits,
|
||||
bool _Constant_iterators = _Traits::__constant_iterators::value>
|
||||
struct _Insert;
|
||||
|
||||
/// Specialization.
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
typename _Hash, typename _RangeHash, typename _Unused,
|
||||
typename _RehashPolicy, typename _Traits>
|
||||
struct _Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_Hash, _RangeHash, _Unused,
|
||||
_RehashPolicy, _Traits, true>
|
||||
: public _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>
|
||||
{
|
||||
using __base_type = _Insert_base<_Key, _Value, _Alloc, _ExtractKey,
|
||||
_Equal, _Hash, _RangeHash, _Unused,
|
||||
_RehashPolicy, _Traits>;
|
||||
|
||||
using value_type = typename __base_type::value_type;
|
||||
using iterator = typename __base_type::iterator;
|
||||
using const_iterator = typename __base_type::const_iterator;
|
||||
using __ireturn_type = typename __base_type::__ireturn_type;
|
||||
|
||||
using __unique_keys = typename __base_type::__unique_keys;
|
||||
using __hashtable = typename __base_type::__hashtable;
|
||||
using __node_gen_type = typename __base_type::__node_gen_type;
|
||||
|
||||
using __base_type::insert;
|
||||
|
||||
__ireturn_type
|
||||
insert(value_type&& __v)
|
||||
{
|
||||
__hashtable& __h = this->_M_conjure_hashtable();
|
||||
__node_gen_type __node_gen(__h);
|
||||
return __h._M_insert(std::move(__v), __node_gen, __unique_keys{});
|
||||
}
|
||||
|
||||
iterator
|
||||
insert(const_iterator __hint, value_type&& __v)
|
||||
{
|
||||
__hashtable& __h = this->_M_conjure_hashtable();
|
||||
__node_gen_type __node_gen(__h);
|
||||
return __h._M_insert(__hint, std::move(__v), __node_gen,
|
||||
__unique_keys{});
|
||||
}
|
||||
};
|
||||
|
||||
/// Specialization.
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
typename _Hash, typename _RangeHash, typename _Unused,
|
||||
typename _RehashPolicy, typename _Traits>
|
||||
struct _Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits, false>
|
||||
: public _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>
|
||||
{
|
||||
using __base_type = _Insert_base<_Key, _Value, _Alloc, _ExtractKey,
|
||||
_Equal, _Hash, _RangeHash, _Unused,
|
||||
_RehashPolicy, _Traits>;
|
||||
using value_type = typename __base_type::value_type;
|
||||
using iterator = typename __base_type::iterator;
|
||||
using const_iterator = typename __base_type::const_iterator;
|
||||
|
||||
using __unique_keys = typename __base_type::__unique_keys;
|
||||
using __hashtable = typename __base_type::__hashtable;
|
||||
using __ireturn_type = typename __base_type::__ireturn_type;
|
||||
|
||||
using __base_type::insert;
|
||||
|
||||
template<typename _Pair>
|
||||
using __is_cons = std::is_constructible<value_type, _Pair&&>;
|
||||
|
||||
template<typename _Pair>
|
||||
using _IFcons = std::enable_if<__is_cons<_Pair>::value>;
|
||||
|
||||
template<typename _Pair>
|
||||
using _IFconsp = typename _IFcons<_Pair>::type;
|
||||
|
||||
template<typename _Pair, typename = _IFconsp<_Pair>>
|
||||
__ireturn_type
|
||||
insert(_Pair&& __v)
|
||||
{
|
||||
__hashtable& __h = this->_M_conjure_hashtable();
|
||||
return __h._M_emplace(__unique_keys{}, std::forward<_Pair>(__v));
|
||||
}
|
||||
|
||||
template<typename _Pair, typename = _IFconsp<_Pair>>
|
||||
iterator
|
||||
insert(const_iterator __hint, _Pair&& __v)
|
||||
{
|
||||
__hashtable& __h = this->_M_conjure_hashtable();
|
||||
return __h._M_emplace(__hint, __unique_keys{},
|
||||
std::forward<_Pair>(__v));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename _Policy>
|
||||
using __has_load_factor = typename _Policy::__has_load_factor;
|
||||
|
||||
|
@ -47,7 +47,8 @@ test01()
|
||||
VERIFY( um.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
// Allocated another node and a pair<const std::string, std::string>:
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 7 );
|
||||
}
|
||||
|
||||
void
|
||||
@ -67,7 +68,8 @@ test02()
|
||||
VERIFY( um.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
// Allocated another node and a pair<const std::string, std::string>:
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 7 );
|
||||
}
|
||||
|
||||
std::size_t
|
||||
@ -96,7 +98,8 @@ test11()
|
||||
VERIFY( um.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
// Allocated another node and a pair<const std::string, std::string>:
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 7 );
|
||||
}
|
||||
|
||||
std::size_t
|
||||
@ -124,7 +127,8 @@ test12()
|
||||
VERIFY( um.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
// Allocated another node and a pair<const std::string, std::string>:
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 7 );
|
||||
}
|
||||
|
||||
struct hash_string_functor
|
||||
@ -154,7 +158,8 @@ test21()
|
||||
VERIFY( um.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
// Allocated another node and a pair<const std::string, std::string>:
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 7 );
|
||||
}
|
||||
|
||||
struct hash_string_view_noexcept_functor
|
||||
@ -184,7 +189,8 @@ test22()
|
||||
VERIFY( um.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
// Allocated another node and a pair<const std::string, std::string>:
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 7 );
|
||||
}
|
||||
|
||||
struct hash_string_view_functor
|
||||
@ -214,7 +220,8 @@ test23()
|
||||
VERIFY( um.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
// Allocated another node and a pair<const std::string, std::string>:
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 7 );
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -47,7 +47,7 @@ test01()
|
||||
VERIFY( us.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 3 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
}
|
||||
|
||||
void
|
||||
@ -67,7 +67,7 @@ test02()
|
||||
VERIFY( us.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 3 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
}
|
||||
|
||||
std::size_t
|
||||
@ -96,7 +96,7 @@ test11()
|
||||
VERIFY( us.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 3 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
}
|
||||
|
||||
std::size_t
|
||||
@ -125,7 +125,7 @@ test12()
|
||||
VERIFY( us.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 3 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
}
|
||||
|
||||
struct hash_string_functor
|
||||
@ -155,7 +155,7 @@ test21()
|
||||
VERIFY( us.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 3 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
}
|
||||
|
||||
struct hash_string_view_noexcept_functor
|
||||
@ -185,7 +185,7 @@ test22()
|
||||
VERIFY( us.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 3 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
}
|
||||
|
||||
struct hash_string_view_functor
|
||||
@ -215,7 +215,7 @@ test23()
|
||||
VERIFY( us.size() == 1 );
|
||||
|
||||
VERIFY( __gnu_test::counter::count() == 3 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 4 );
|
||||
VERIFY( __gnu_test::counter::get()._M_increments == 5 );
|
||||
}
|
||||
|
||||
void
|
||||
|
Loading…
Reference in New Issue
Block a user