Implement C++17 node extraction and insertion (P0083R5)
authorJonathan Wakely <jwakely@redhat.com>
Thu, 22 Sep 2016 13:58:49 +0000 (14:58 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 22 Sep 2016 13:58:49 +0000 (14:58 +0100)
* doc/xml/manual/status_cxx2017.xml: Document status.
* doc/html/*: Regenerate.
* include/Makefile.am: Add bits/node_handle.h and reorder.
* include/Makefile.in: Regenerate.
* include/bits/hashtable.h (_Hashtable::node_type)
(_Hashtable::insert_return_type, _Hashtable::_M_reinsert_node)
(_Hashtable::_M_reinsert_node_multi, _Hashtable::extract)
(_Hashtable::_M_merge_unique, _Hashtable::_M_merge_multi): Define.
(_Hash_merge_helper): Define primary template.
* include/bits/node_handle.h: New header.
* include/bits/stl_map.h (map): Declare _Rb_tree_merge_helper as
friend.
(map::node_type, map::insert_return_type, map::extract, map::merge)
(map::insert(node_type&&), map::insert(const_iterator, node_type&&)):
Define new members.
(_Rb_tree_merge_helper): Specialize for map.
* include/bits/stl_multimap.h (multimap): Declare _Rb_tree_merge_helper
as friend.
(multimap::node_type, multimap::extract, multimap::merge)
(multimap::insert(node_type&&))
(multimap::insert(const_iterator, node_type&&)): Define.
(_Rb_tree_merge_helper): Specialize for multimap.
* include/bits/stl_multiset.h (multiset): Declare _Rb_tree_merge_helper
as friend.
(multiset::node_type, multiset::extract, multiset::merge)
(multiset::insert(node_type&&))
(multiset::insert(const_iterator, node_type&&)): Define.
* include/bits/stl_set.h (set): Declare _Rb_tree_merge_helper as
friend.
(set::node_type, set::insert_return_type, set::extract, set::merge)
(set::insert(node_type&&), set::insert(const_iterator, node_type&&)):
Define.
(_Rb_tree_merge_helper): Specialize for set.
* include/bits/stl_tree.h (_Rb_tree): Declare _Rb_tree<> as friend.
(_Rb_tree::node_type, _Rb_tree::insert_return_type)
(_Rb_tree::_M_reinsert_node_unique, _Rb_tree::_M_reinsert_node_equal)
(_Rb_tree::_M_reinsert_node_hint_unique)
(_Rb_tree::_M_reinsert_node_hint_equal, _Rb_tree::extract)
(_Rb_tree::_M_merge_unique, _Rb_tree::_M_merge_equal): Define.
(_Rb_tree_merge_helper): Specialize for multiset.
* include/bits/unordered_map.h (unordered_map): Declare
unordered_map<> and unordered_multimap<> as friends.
(unordered_map::node_type, unordered_map::insert_return_type)
(unordered_map::extract, unordered_map::merge)
(unordered_map::insert(node_type&&))
(unordered_map::insert(const_iterator, node_type&&))
(unordered_multimap): Declare _Hash_merge_helper as friend.
(unordered_multimap::node_type, unordered_multimap::extract)
(unordered_multimap::merge, unordered_multimap::insert(node_type&&))
(unordered_multimap::insert(const_iterator, node_type&&)): Define.
(_Hash_merge_helper): Specialize for unordered maps and multimaps.
* include/bits/unordered_set.h (unordered_set, unordered_multiset):
Declare _Hash_merge_helper as friend.
(unordered_set::node_type, unordered_set::insert_return_type)
(unordered_set::extract, unordered_set::merge)
(unordered_set::insert(node_type&&))
(unordered_set::insert(const_iterator, node_type&&)): Define.
(unordered_multiset::node_type, unordered_multiset::extract)
(unordered_multiset::merge, unordered_multiset::insert(node_type&&))
(unordered_multiset::insert(const_iterator, node_type&&)): Define.
(_Hash_merge_helper): Specialize for unordered sets and multisets.
* include/debug/map.h (map): Add using declarations or forwarding
functions for new members.
* include/debug/map.h (multimap): Likewise.
* include/debug/map.h (multiset): Likewise.
* include/debug/map.h (set): Likewise.
* include/debug/unordered_map (unordered_map, unordered_multimap):
Likewise.
* include/debug/unordered_set( unordered_set, unordered_multiset):
Likewise.
* python/libstdcxx/v6/printers.py (get_value_from_aligned_membuf): New
helper function.
(get_value_from_list_node, get_value_from_Rb_tree_node): Use helper.
(StdNodeHandlePrinter): Define printer for node handles.
(build_libstdcxx_dictionary): Register StdNodeHandlePrinter.
* testsuite/23_containers/map/modifiers/extract.cc: New.
* testsuite/23_containers/map/modifiers/merge.cc: New.
* testsuite/23_containers/multimap/modifiers/extract.cc: New.
* testsuite/23_containers/multimap/modifiers/merge.cc: New.
* testsuite/23_containers/multiset/modifiers/extract.cc: New.
* testsuite/23_containers/multiset/modifiers/merge.cc: New.
* testsuite/23_containers/set/modifiers/extract.cc: New.
* testsuite/23_containers/set/modifiers/merge.cc: New.
* testsuite/23_containers/unordered_map/modifiers/extract.cc: New.
* testsuite/23_containers/unordered_map/modifiers/merge.cc: New.
* testsuite/23_containers/unordered_multimap/modifiers/extract.cc:
New.
* testsuite/23_containers/unordered_multimap/modifiers/merge.cc: New.
* testsuite/23_containers/unordered_multiset/modifiers/extract.cc:
New.
* testsuite/23_containers/unordered_multiset/modifiers/merge.cc: New.
* testsuite/23_containers/unordered_set/modifiers/extract.cc: New.
* testsuite/23_containers/unordered_set/modifiers/merge.cc: New.
* testsuite/23_containers/unordered_set/instantiation_neg.cc: Adjust
dg-error lineno.
* testsuite/libstdc++-prettyprinters/cxx17.cc: Test node handles.

From-SVN: r240363

40 files changed:
libstdc++-v3/ChangeLog
libstdc++-v3/doc/html/manual/profile_mode_devel.html
libstdc++-v3/doc/html/manual/status.html
libstdc++-v3/doc/xml/manual/status_cxx2017.xml
libstdc++-v3/include/Makefile.am
libstdc++-v3/include/Makefile.in
libstdc++-v3/include/bits/hashtable.h
libstdc++-v3/include/bits/node_handle.h [new file with mode: 0644]
libstdc++-v3/include/bits/stl_map.h
libstdc++-v3/include/bits/stl_multimap.h
libstdc++-v3/include/bits/stl_multiset.h
libstdc++-v3/include/bits/stl_set.h
libstdc++-v3/include/bits/stl_tree.h
libstdc++-v3/include/bits/unordered_map.h
libstdc++-v3/include/bits/unordered_set.h
libstdc++-v3/include/debug/map.h
libstdc++-v3/include/debug/multimap.h
libstdc++-v3/include/debug/multiset.h
libstdc++-v3/include/debug/set.h
libstdc++-v3/include/debug/unordered_map
libstdc++-v3/include/debug/unordered_set
libstdc++-v3/python/libstdcxx/v6/printers.py
libstdc++-v3/testsuite/23_containers/map/modifiers/extract.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/map/modifiers/merge.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/multimap/modifiers/extract.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/multimap/modifiers/merge.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/multiset/modifiers/extract.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/multiset/modifiers/merge.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/set/modifiers/extract.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/set/modifiers/merge.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/unordered_map/modifiers/extract.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/unordered_map/modifiers/merge.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/unordered_multimap/modifiers/extract.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/unordered_multimap/modifiers/merge.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/unordered_multiset/modifiers/extract.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/unordered_multiset/modifiers/merge.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/unordered_set/instantiation_neg.cc
libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/extract.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/merge.cc [new file with mode: 0644]
libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc

index 16a4954..9376bfb 100644 (file)
@@ -1,3 +1,103 @@
+2016-09-22  Jonathan Wakely  <jwakely@redhat.com>
+
+       Implement C++17 node extraction and insertion (P0083R5)
+       * doc/xml/manual/status_cxx2017.xml: Document status.
+       * doc/html/*: Regenerate.
+       * include/Makefile.am: Add bits/node_handle.h and reorder.
+       * include/Makefile.in: Regenerate.
+       * include/bits/hashtable.h (_Hashtable::node_type)
+       (_Hashtable::insert_return_type, _Hashtable::_M_reinsert_node)
+       (_Hashtable::_M_reinsert_node_multi, _Hashtable::extract)
+       (_Hashtable::_M_merge_unique, _Hashtable::_M_merge_multi): Define.
+       (_Hash_merge_helper): Define primary template.
+       * include/bits/node_handle.h: New header.
+       * include/bits/stl_map.h (map): Declare _Rb_tree_merge_helper as
+       friend.
+       (map::node_type, map::insert_return_type, map::extract, map::merge)
+       (map::insert(node_type&&), map::insert(const_iterator, node_type&&)):
+       Define new members.
+       (_Rb_tree_merge_helper): Specialize for map.
+       * include/bits/stl_multimap.h (multimap): Declare _Rb_tree_merge_helper
+       as friend.
+       (multimap::node_type, multimap::extract, multimap::merge)
+       (multimap::insert(node_type&&))
+       (multimap::insert(const_iterator, node_type&&)): Define.
+       (_Rb_tree_merge_helper): Specialize for multimap.
+       * include/bits/stl_multiset.h (multiset): Declare _Rb_tree_merge_helper
+       as friend.
+       (multiset::node_type, multiset::extract, multiset::merge)
+       (multiset::insert(node_type&&))
+       (multiset::insert(const_iterator, node_type&&)): Define.
+       * include/bits/stl_set.h (set): Declare _Rb_tree_merge_helper as
+       friend.
+       (set::node_type, set::insert_return_type, set::extract, set::merge)
+       (set::insert(node_type&&), set::insert(const_iterator, node_type&&)):
+       Define.
+       (_Rb_tree_merge_helper): Specialize for set.
+       * include/bits/stl_tree.h (_Rb_tree): Declare _Rb_tree<> as friend.
+       (_Rb_tree::node_type, _Rb_tree::insert_return_type)
+       (_Rb_tree::_M_reinsert_node_unique, _Rb_tree::_M_reinsert_node_equal)
+       (_Rb_tree::_M_reinsert_node_hint_unique)
+       (_Rb_tree::_M_reinsert_node_hint_equal, _Rb_tree::extract)
+       (_Rb_tree::_M_merge_unique, _Rb_tree::_M_merge_equal): Define.
+       (_Rb_tree_merge_helper): Specialize for multiset.
+       * include/bits/unordered_map.h (unordered_map): Declare
+       unordered_map<> and unordered_multimap<> as friends.
+       (unordered_map::node_type, unordered_map::insert_return_type)
+       (unordered_map::extract, unordered_map::merge)
+       (unordered_map::insert(node_type&&))
+       (unordered_map::insert(const_iterator, node_type&&))
+       (unordered_multimap): Declare _Hash_merge_helper as friend.
+       (unordered_multimap::node_type, unordered_multimap::extract)
+       (unordered_multimap::merge, unordered_multimap::insert(node_type&&))
+       (unordered_multimap::insert(const_iterator, node_type&&)): Define.
+       (_Hash_merge_helper): Specialize for unordered maps and multimaps.
+       * include/bits/unordered_set.h (unordered_set, unordered_multiset):
+       Declare _Hash_merge_helper as friend.
+       (unordered_set::node_type, unordered_set::insert_return_type)
+       (unordered_set::extract, unordered_set::merge)
+       (unordered_set::insert(node_type&&))
+       (unordered_set::insert(const_iterator, node_type&&)): Define.
+       (unordered_multiset::node_type, unordered_multiset::extract)
+       (unordered_multiset::merge, unordered_multiset::insert(node_type&&))
+       (unordered_multiset::insert(const_iterator, node_type&&)): Define.
+       (_Hash_merge_helper): Specialize for unordered sets and multisets.
+       * include/debug/map.h (map): Add using declarations or forwarding
+       functions for new members.
+       * include/debug/map.h (multimap): Likewise.
+       * include/debug/map.h (multiset): Likewise.
+       * include/debug/map.h (set): Likewise.
+       * include/debug/unordered_map (unordered_map, unordered_multimap):
+       Likewise.
+       * include/debug/unordered_set( unordered_set, unordered_multiset):
+       Likewise.
+       * python/libstdcxx/v6/printers.py (get_value_from_aligned_membuf): New
+       helper function.
+       (get_value_from_list_node, get_value_from_Rb_tree_node): Use helper.
+       (StdNodeHandlePrinter): Define printer for node handles.
+       (build_libstdcxx_dictionary): Register StdNodeHandlePrinter.
+       * testsuite/23_containers/map/modifiers/extract.cc: New.
+       * testsuite/23_containers/map/modifiers/merge.cc: New.
+       * testsuite/23_containers/multimap/modifiers/extract.cc: New.
+       * testsuite/23_containers/multimap/modifiers/merge.cc: New.
+       * testsuite/23_containers/multiset/modifiers/extract.cc: New.
+       * testsuite/23_containers/multiset/modifiers/merge.cc: New.
+       * testsuite/23_containers/set/modifiers/extract.cc: New.
+       * testsuite/23_containers/set/modifiers/merge.cc: New.
+       * testsuite/23_containers/unordered_map/modifiers/extract.cc: New.
+       * testsuite/23_containers/unordered_map/modifiers/merge.cc: New.
+       * testsuite/23_containers/unordered_multimap/modifiers/extract.cc:
+       New.
+       * testsuite/23_containers/unordered_multimap/modifiers/merge.cc: New.
+       * testsuite/23_containers/unordered_multiset/modifiers/extract.cc:
+       New.
+       * testsuite/23_containers/unordered_multiset/modifiers/merge.cc: New.
+       * testsuite/23_containers/unordered_set/modifiers/extract.cc: New.
+       * testsuite/23_containers/unordered_set/modifiers/merge.cc: New.
+       * testsuite/23_containers/unordered_set/instantiation_neg.cc: Adjust
+       dg-error lineno.
+       * testsuite/libstdc++-prettyprinters/cxx17.cc: Test node handles.
+
 2016-09-22  Ville Voutilainen  <ville.voutilainen@gmail.com>
 
        Fix tests on old arm platforms for optional.
index 2521dd6..db96f98 100644 (file)
@@ -64,4 +64,4 @@
    <code class="code">include/profile/impl/profiler_trace.h</code>.  Use
    <code class="code">__trace_vector_to_list</code> as an example.
   </p><p>Add documentation in file <code class="code">doc/xml/manual/profile_mode.xml</code>.
-  </p></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="profile_mode_impl.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="profile_mode.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="profile_mode_diagnostics.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Implementation Issues </td><td width="20%" align="center"><a accesskey="h" href="../index.html">Home</a></td><td width="40%" align="right" valign="top"> Diagnostics</td></tr></table></div></body></html>
+  </p></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="profile_mode_impl.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="profile_mode.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="profile_mode_diagnostics.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Implementation Issues </td><td width="20%" align="center"><a accesskey="h" href="../index.html">Home</a></td><td width="40%" align="right" valign="top"> Diagnostics</td></tr></table></div></body></html>
\ No newline at end of file
index e82739a..3c58979 100644 (file)
@@ -666,11 +666,11 @@ Feature-testing recommendations for C++</a>.
        </a>
       </td><td align="center"> 6.1 </td><td align="left"><code class="code"> __cpp_lib_map_try_emplace &gt;= 201411</code>,
             <code class="code"> __cpp_lib_unordered_map_try_emplace &gt;= 201411</code>
-      </td></tr><tr bgcolor="#C8B0B0"><td align="left"> Splicing Maps and Sets </td><td align="left">
+      </td></tr><tr><td align="left"> Splicing Maps and Sets </td><td align="left">
        <a class="link" href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0083r3.pdf" target="_top">
        P0083R3
        </a>
-      </td><td align="center"> No </td><td align="left"><code class="code"> __cpp_lib_node_extract &gt;= 201606 </code></td></tr><tr><td align="left">Non-member <code class="code">size()</code> and more</td><td align="left">
+      </td><td align="center"> 7 </td><td align="left"><code class="code"> __cpp_lib_node_extract &gt;= 201606 </code></td></tr><tr><td align="left">Non-member <code class="code">size()</code> and more</td><td align="left">
        <a class="link" href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4280.pdf" target="_top">
          N4280
        </a>
index ff96627..76eaaa0 100644 (file)
@@ -562,14 +562,13 @@ Feature-testing recommendations for C++</link>.
     </row>
 
     <row>
-      <?dbhtml bgcolor="#C8B0B0" ?>
       <entry> Splicing Maps and Sets </entry>
       <entry>
        <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0083r3.pdf">
        P0083R3
        </link>
       </entry>
-      <entry align="center"> No </entry>
+      <entry align="center"> 7 </entry>
       <entry><code> __cpp_lib_node_extract >= 201606 </code></entry>
     </row>
 
index 9cd18df..7782258 100644 (file)
@@ -129,7 +129,7 @@ bits_headers = \
        ${bits_srcdir}/specfun.h \
        ${bits_srcdir}/memoryfwd.h \
        ${bits_srcdir}/move.h \
-       ${bits_srcdir}/std_mutex.h \
+       ${bits_srcdir}/node_handle.h \
        ${bits_srcdir}/ostream.tcc \
        ${bits_srcdir}/ostream_insert.h \
        ${bits_srcdir}/parse_numbers.h \
@@ -159,6 +159,7 @@ bits_headers = \
        ${bits_srcdir}/shared_ptr_base.h \
        ${bits_srcdir}/slice_array.h \
        ${bits_srcdir}/sstream.tcc \
+       ${bits_srcdir}/std_mutex.h \
        ${bits_srcdir}/stl_algo.h \
        ${bits_srcdir}/stl_algobase.h \
        ${bits_srcdir}/stl_bvector.h \
index d799860..502a89e 100644 (file)
@@ -419,7 +419,7 @@ bits_headers = \
        ${bits_srcdir}/specfun.h \
        ${bits_srcdir}/memoryfwd.h \
        ${bits_srcdir}/move.h \
-       ${bits_srcdir}/std_mutex.h \
+       ${bits_srcdir}/node_handle.h \
        ${bits_srcdir}/ostream.tcc \
        ${bits_srcdir}/ostream_insert.h \
        ${bits_srcdir}/parse_numbers.h \
@@ -449,6 +449,7 @@ bits_headers = \
        ${bits_srcdir}/shared_ptr_base.h \
        ${bits_srcdir}/slice_array.h \
        ${bits_srcdir}/sstream.tcc \
+       ${bits_srcdir}/std_mutex.h \
        ${bits_srcdir}/stl_algo.h \
        ${bits_srcdir}/stl_algobase.h \
        ${bits_srcdir}/stl_bvector.h \
index 6d7134a..be67741 100644 (file)
@@ -33,6 +33,9 @@
 #pragma GCC system_header
 
 #include <bits/hashtable_policy.h>
+#if __cplusplus > 201402L
+# include <bits/node_handle.h>
+#endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -308,6 +311,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       using const_local_iterator = typename __hashtable_base::
                                   const_local_iterator;
 
+#if __cplusplus > 201402L
+      using node_type = _Node_handle<_Key, _Value, __node_alloc_type>;
+      using insert_return_type = _Node_insert_return<iterator, node_type>;
+#endif
+
     private:
       __bucket_type*           _M_buckets              = &_M_single_bucket;
       size_type                        _M_bucket_count         = 1;
@@ -762,6 +770,135 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // DR 1189.
       // reserve, if present, comes from _Rehash_base.
 
+#if __cplusplus > 201402L
+      /// Re-insert an extracted node into a container with unique keys.
+      insert_return_type
+      _M_reinsert_node(node_type&& __nh)
+      {
+       insert_return_type __ret;
+       if (__nh.empty())
+         __ret.position = end();
+       else
+         {
+           __glibcxx_assert(get_allocator() == __nh.get_allocator());
+
+           const key_type& __k = __nh._M_key();
+           __hash_code __code = this->_M_hash_code(__k);
+           size_type __bkt = _M_bucket_index(__k, __code);
+           if (__node_type* __n = _M_find_node(__bkt, __k, __code))
+             {
+               __ret.node = std::move(__nh);
+               __ret.position = iterator(__n);
+               __ret.inserted = false;
+             }
+           else
+             {
+               __ret.position
+                 = _M_insert_unique_node(__bkt, __code, __nh._M_ptr);
+               __nh._M_ptr = nullptr;
+               __ret.inserted = true;
+             }
+         }
+       return __ret;
+      }
+
+      /// Re-insert an extracted node into a container with equivalent keys.
+      iterator
+      _M_reinsert_node_multi(const_iterator __hint, node_type&& __nh)
+      {
+       iterator __ret;
+       if (__nh.empty())
+         __ret = end();
+       else
+         {
+           __glibcxx_assert(get_allocator() == __nh.get_allocator());
+
+           auto __code = this->_M_hash_code(__nh._M_key());
+           auto __node = std::exchange(__nh._M_ptr, nullptr);
+           // FIXME: this deallocates the node on exception.
+           __ret = _M_insert_multi_node(__hint._M_cur, __code, __node);
+         }
+       return __ret;
+      }
+
+      /// Extract a node.
+      node_type
+      extract(const_iterator __pos)
+      {
+       __node_type* __n = __pos._M_cur;
+       size_t __bkt = _M_bucket_index(__n);
+
+       // Look for previous node to unlink it from the erased one, this
+       // is why we need buckets to contain the before begin to make
+       // this search fast.
+       __node_base* __prev_n = _M_get_previous_node(__bkt, __n);
+
+       if (__prev_n == _M_buckets[__bkt])
+         _M_remove_bucket_begin(__bkt, __n->_M_next(),
+            __n->_M_nxt ? _M_bucket_index(__n->_M_next()) : 0);
+       else if (__n->_M_nxt)
+         {
+           size_type __next_bkt = _M_bucket_index(__n->_M_next());
+           if (__next_bkt != __bkt)
+             _M_buckets[__next_bkt] = __prev_n;
+         }
+
+       __prev_n->_M_nxt = __n->_M_nxt;
+       __n->_M_nxt = nullptr;
+       --_M_element_count;
+       return { __n, this->_M_node_allocator() };
+      }
+
+      /// Extract a node.
+      node_type
+      extract(const _Key& __k)
+      {
+       node_type __nh;
+       auto __pos = find(__k);
+       if (__pos != end())
+         __nh = extract(const_iterator(__pos));
+       return __nh;
+      }
+
+      /// Merge from a compatible container into one with unique keys.
+      template<typename _Compatible_Hashtable>
+       void
+       _M_merge_unique(_Compatible_Hashtable& __src) noexcept
+       {
+         static_assert(is_same_v<typename _Compatible_Hashtable::node_type,
+             node_type>, "Node types are compatible");
+         __glibcxx_assert(get_allocator() == __src.get_allocator());
+
+         for (auto __i = __src.begin(), __end = __src.end(); __i != __end;)
+           {
+             auto __pos = __i++;
+             const key_type& __k = this->_M_extract()(__pos._M_cur->_M_v());
+             __hash_code __code = this->_M_hash_code(__k);
+             size_type __bkt = _M_bucket_index(__k, __code);
+             if (_M_find_node(__bkt, __k, __code) == nullptr)
+               {
+                 auto __nh = __src.extract(__pos);
+                 _M_insert_unique_node(__bkt, __code, __nh._M_ptr);
+                 __nh._M_ptr = nullptr;
+               }
+           }
+       }
+
+      /// Merge from a compatible container into one with equivalent keys.
+      template<typename _Compatible_Hashtable>
+       void
+       _M_merge_multi(_Compatible_Hashtable& __src) noexcept
+       {
+         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++));
+       }
+#endif // C++17
+
     private:
       // Helper rehash method used when keys are unique.
       void _M_rehash_aux(size_type __n, std::true_type);
@@ -2078,6 +2215,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_buckets = __new_buckets;
     }
 
+#if __cplusplus > 201402L
+  template<typename, typename, typename> class _Hash_merge_helper { };
+#endif // C++17
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
diff --git a/libstdc++-v3/include/bits/node_handle.h b/libstdc++-v3/include/bits/node_handle.h
new file mode 100644 (file)
index 0000000..60c2883
--- /dev/null
@@ -0,0 +1,330 @@
+// Node handles for containers -*- C++ -*-
+
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file bits/node_handle.h
+ *  This is an internal header file, included by other library headers.
+ *  Do not attempt to use it directly.
+ *  @headername{map,set,unordered_map,unordered_set}
+ */
+
+#ifndef _NODE_HANDLE
+#define _NODE_HANDLE 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201402L
+# define __cpp_lib_node_extract 201606
+
+#include <optional>
+#include <tuple>
+#include <bits/alloc_traits.h>
+#include <bits/ptr_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  /// Base class for node handle types of maps and sets.
+  template<typename _Val, typename _NodeAlloc>
+    class _Node_handle_common
+    {
+      using _AllocTraits = allocator_traits<_NodeAlloc>;
+
+    public:
+      using allocator_type = __alloc_rebind<_NodeAlloc, _Val>;
+
+      allocator_type
+      get_allocator() const noexcept
+      {
+       __glibcxx_assert(!this->empty());
+       return allocator_type(*_M_alloc);
+      }
+
+      explicit operator bool() const noexcept { return _M_ptr != nullptr; }
+
+      bool empty() const noexcept { return _M_ptr == nullptr; }
+
+    protected:
+      constexpr _Node_handle_common() noexcept : _M_ptr(), _M_alloc() {}
+
+      ~_Node_handle_common() { _M_destroy(); }
+
+      _Node_handle_common(_Node_handle_common&& __nh) noexcept
+      : _M_ptr(__nh._M_ptr), _M_alloc(std::move(__nh._M_alloc))
+      {
+       __nh._M_ptr = nullptr;
+       __nh._M_alloc = nullopt;
+      }
+
+      _Node_handle_common&
+      operator=(_Node_handle_common&& __nh) noexcept
+      {
+       _M_destroy();
+       _M_ptr = __nh._M_ptr;
+       if constexpr (is_move_assignable_v<_NodeAlloc>)
+         {
+           if (_AllocTraits::propagate_on_container_move_assignment::value
+               || !this->_M_alloc)
+             this->_M_alloc = std::move(__nh._M_alloc);
+           else
+             __glibcxx_assert(this->_M_alloc == __nh._M_alloc);
+         }
+       else
+         __glibcxx_assert(_M_alloc);
+       __nh._M_ptr = nullptr;
+       __nh._M_alloc = nullopt;
+       return *this;
+      }
+
+      _Node_handle_common(typename _AllocTraits::pointer __ptr,
+                         const _NodeAlloc& __alloc)
+      : _M_ptr(__ptr), _M_alloc(__alloc) { }
+
+      void
+      _M_swap(_Node_handle_common& __nh) noexcept
+      {
+       using std::swap;
+       swap(_M_ptr, __nh._M_ptr);
+       if (_AllocTraits::propagate_on_container_swap
+           || !_M_alloc || !__nh._M_alloc)
+         _M_alloc.swap(__nh._M_alloc);
+       else
+         __glibcxx_assert(_M_alloc == __nh._M_alloc);
+      }
+
+    private:
+      void
+      _M_destroy() noexcept
+      {
+       if (_M_ptr != nullptr)
+         {
+           allocator_type __alloc(*_M_alloc);
+           allocator_traits<allocator_type>::destroy(__alloc,
+                                                     _M_ptr->_M_valptr());
+           _AllocTraits::deallocate(*_M_alloc, _M_ptr, 1);
+         }
+      }
+
+    protected:
+      typename _AllocTraits::pointer   _M_ptr;
+    private:
+      optional<_NodeAlloc>             _M_alloc;
+
+      template<typename _Key2, typename _Value2, typename _KeyOfValue,
+              typename _Compare, typename _ValueAlloc>
+       friend class _Rb_tree;
+    };
+
+  /// Node handle type for maps.
+  template<typename _Key, typename _Value, typename _NodeAlloc>
+    class _Node_handle : public _Node_handle_common<_Value, _NodeAlloc>
+    {
+    public:
+      constexpr _Node_handle() noexcept = default;
+      ~_Node_handle() = default;
+      _Node_handle(_Node_handle&&) noexcept = default;
+
+      _Node_handle&
+      operator=(_Node_handle&&) noexcept = default;
+
+      using key_type = _Key;
+      using mapped_type = typename _Value::second_type;
+
+      key_type&
+      key() const noexcept
+      {
+       __glibcxx_assert(!this->empty());
+       return *_M_pkey;
+      }
+
+      mapped_type&
+      mapped() const noexcept
+      {
+       __glibcxx_assert(!this->empty());
+       return *_M_pmapped;
+      }
+
+      void
+      swap(_Node_handle& __nh) noexcept
+      {
+       this->_M_swap(__nh);
+       using std::swap;
+       swap(_M_pkey, __nh._M_pkey);
+       swap(_M_pmapped, __nh._M_pmapped);
+      }
+
+      friend void
+      swap(_Node_handle& __x, _Node_handle& __y)
+      noexcept(noexcept(__x.swap(__y)))
+      { __x.swap(__y); }
+
+    private:
+      using _AllocTraits = allocator_traits<_NodeAlloc>;
+
+      using _PtrTraits = pointer_traits<typename _NodeAlloc::pointer>;
+
+      _Node_handle(typename _AllocTraits::pointer __ptr,
+                          const _NodeAlloc& __alloc)
+      : _Node_handle_common<_Value, _NodeAlloc>(__ptr, __alloc)
+      {
+       if (__ptr)
+         {
+           auto& __key = const_cast<_Key&>(__ptr->_M_valptr()->first);
+           _M_pkey = _S_pointer_to(__key);
+           _M_pmapped = _S_pointer_to(__ptr->_M_valptr()->second);
+         }
+       else
+         {
+           _M_pkey = nullptr;
+           _M_pmapped = nullptr;
+         }
+      }
+
+      template<typename _Tp>
+       using __pointer = __ptr_rebind<typename _AllocTraits::pointer, _Tp>;
+
+      __pointer<_Key>                          _M_pkey = nullptr;
+      __pointer<typename _Value::second_type>  _M_pmapped = nullptr;
+
+      template<typename _Tp>
+       __pointer<_Tp>
+       _S_pointer_to(_Tp& __obj)
+       { return pointer_traits<__pointer<_Tp>>::pointer_to(__obj); }
+
+      const key_type&
+      _M_key() const noexcept { return key(); }
+
+      template<typename _Key2, typename _Value2, typename _KeyOfValue,
+              typename _Compare, typename _ValueAlloc>
+       friend class _Rb_tree;
+
+      template<typename _Key2, typename _Value2, typename _ValueAlloc,
+              typename _ExtractKey, typename _Equal,
+              typename _H1, typename _H2, typename _Hash,
+              typename _RehashPolicy, typename _Traits>
+       friend class _Hashtable;
+    };
+
+  /// Node handle type for sets.
+  template<typename _Value, typename _NodeAlloc>
+    class _Node_handle<_Value, _Value, _NodeAlloc>
+    : public _Node_handle_common<_Value, _NodeAlloc>
+    {
+    public:
+      constexpr _Node_handle() noexcept = default;
+      ~_Node_handle() = default;
+      _Node_handle(_Node_handle&&) noexcept = default;
+
+      _Node_handle&
+      operator=(_Node_handle&&) noexcept = default;
+
+      using value_type = _Value;
+
+      value_type&
+      value() const noexcept
+      {
+       __glibcxx_assert(!this->empty());
+       return *this->_M_ptr->_M_valptr();
+      }
+
+      void
+      swap(_Node_handle& __nh) noexcept
+      { this->_M_swap(__nh); }
+
+      friend void
+      swap(_Node_handle& __x, _Node_handle& __y)
+      noexcept(noexcept(__x.swap(__y)))
+      { __x.swap(__y); }
+
+    private:
+      using _AllocTraits = allocator_traits<_NodeAlloc>;
+
+      _Node_handle(typename _AllocTraits::pointer __ptr,
+                          const _NodeAlloc& __alloc)
+      : _Node_handle_common<_Value, _NodeAlloc>(__ptr, __alloc) { }
+
+      const value_type&
+      _M_key() const noexcept { return value(); }
+
+      template<typename _Key, typename _Val, typename _KeyOfValue,
+              typename _Compare, typename _Alloc>
+       friend class _Rb_tree;
+
+      template<typename _Key2, typename _Value2, typename _ValueAlloc,
+              typename _ExtractKey, typename _Equal,
+              typename _H1, typename _H2, typename _Hash,
+              typename _RehashPolicy, typename _Traits>
+       friend class _Hashtable;
+    };
+
+  /// Return type of insert(node_handle&&) on unique maps/sets.
+  template<typename _Iterator, typename _NodeHandle>
+    struct _Node_insert_return
+    {
+      bool             inserted = false;
+      _Iterator                position = _Iterator();
+      _NodeHandle      node;
+
+      template<size_t _Idx>
+       decltype(auto) get() &
+       { return std::get<_Idx>(std::tie(inserted, position, node)); }
+
+      template<size_t _Idx>
+       decltype(auto) get() const &
+       { return std::get<_Idx>(std::tie(inserted, position, node)); }
+
+      template<size_t _Idx>
+       decltype(auto) get() &&
+       {
+         return std::move(std::get<_Idx>(std::tie(inserted, position, node)));
+       }
+
+      template<size_t _Idx>
+       decltype(auto) get() const &&
+       {
+         return std::move(std::get<_Idx>(std::tie(inserted, position, node)));
+       }
+    };
+
+  template<typename _Iterator, typename _NodeHandle>
+    struct tuple_size<_Node_insert_return<_Iterator, _NodeHandle>>
+    : integral_constant<size_t, 3> { };
+
+  template<typename _Iterator, typename _NodeHandle>
+    struct tuple_element<0, _Node_insert_return<_Iterator, _NodeHandle>>
+    { using type = bool; };
+
+  template<typename _Iterator, typename _NodeHandle>
+    struct tuple_element<1, _Node_insert_return<_Iterator, _NodeHandle>>
+    { using type = _Iterator; };
+
+  template<typename _Iterator, typename _NodeHandle>
+    struct tuple_element<2, _Node_insert_return<_Iterator, _NodeHandle>>
+    { using type = _NodeHandle; };
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+
+#endif // C++17
+#endif
index 29bade7..f9482e2 100644 (file)
@@ -67,6 +67,9 @@ namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
+  template <typename _Key, typename _Tp, typename _Compare, typename _Alloc>
+    class multimap;
+
   /**
    *  @brief A standard container made up of (key,value) pairs, which can be
    *  retrieved based on a key, in logarithmic time.
@@ -153,6 +156,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       typedef typename _Rep_type::reverse_iterator       reverse_iterator;
       typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator;
 
+#if __cplusplus > 201402L
+      using node_type = typename _Rep_type::node_type;
+      using insert_return_type = typename _Rep_type::insert_return_type;
+#endif
+
       // [23.3.1.1] construct/copy/destroy
       // (get_allocator() is also listed in this section)
 
@@ -594,6 +602,57 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 #endif
 
 #if __cplusplus > 201402L
+      /// Extract a node.
+      node_type
+      extract(const_iterator __pos)
+      { return _M_t.extract(__pos); }
+
+      /// Extract a node.
+      node_type
+      extract(const key_type& __x)
+      { return _M_t.extract(__x); }
+
+      /// Re-insert an extracted node.
+      insert_return_type
+      insert(node_type&& __nh)
+      { return _M_t._M_reinsert_node_unique(std::move(__nh)); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      { return _M_t._M_reinsert_node_hint_unique(__hint, std::move(__nh)); }
+
+      template<typename, typename>
+       friend class _Rb_tree_merge_helper;
+
+      template<typename _C2>
+       void
+       merge(map<_Key, _Tp, _C2, _Alloc>& __source)
+       {
+         using _Merge_helper = _Rb_tree_merge_helper<map, _C2>;
+         _M_t._M_merge_unique(_Merge_helper::_S_get_tree(__source));
+       }
+
+      template<typename _C2>
+       void
+       merge(map<_Key, _Tp, _C2, _Alloc>&& __source)
+       { merge(__source); }
+
+      template<typename _C2>
+       void
+       merge(multimap<_Key, _Tp, _C2, _Alloc>& __source)
+       {
+         using _Merge_helper = _Rb_tree_merge_helper<map, _C2>;
+         _M_t._M_merge_unique(_Merge_helper::_S_get_tree(__source));
+       }
+
+      template<typename _C2>
+       void
+       merge(multimap<_Key, _Tp, _C2, _Alloc>&& __source)
+       { merge(__source); }
+#endif // C++17
+
+#if __cplusplus > 201402L
 #define __cpp_lib_map_try_emplace 201411
       /**
        *  @brief Attempts to build and insert a std::pair into the %map.
@@ -1365,6 +1424,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     { __x.swap(__y); }
 
 _GLIBCXX_END_NAMESPACE_CONTAINER
+
+#if __cplusplus > 201402L
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+  // Allow std::map access to internals of compatible maps.
+  template<typename _Key, typename _Val, typename _Cmp1, typename _Alloc,
+          typename _Cmp2>
+    struct
+    _Rb_tree_merge_helper<_GLIBCXX_STD_C::map<_Key, _Val, _Cmp1, _Alloc>,
+                         _Cmp2>
+    {
+    private:
+      friend class _GLIBCXX_STD_C::map<_Key, _Val, _Cmp1, _Alloc>;
+
+      static auto&
+      _S_get_tree(_GLIBCXX_STD_C::map<_Key, _Val, _Cmp2, _Alloc>& __map)
+      { return __map._M_t; }
+
+      static auto&
+      _S_get_tree(_GLIBCXX_STD_C::multimap<_Key, _Val, _Cmp2, _Alloc>& __map)
+      { return __map._M_t; }
+    };
+_GLIBCXX_END_NAMESPACE_VERSION
+#endif // C++17
+
 } // namespace std
 
 #endif /* _STL_MAP_H */
index 0bb379f..2b56824 100644 (file)
@@ -65,6 +65,9 @@ namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
+  template <typename _Key, typename _Tp, typename _Compare, typename _Alloc>
+    class map;
+
   /**
    *  @brief A standard container made up of (key,value) pairs, which can be
    *  retrieved based on a key, in logarithmic time.
@@ -151,6 +154,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       typedef typename _Rep_type::reverse_iterator       reverse_iterator;
       typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator;
 
+#if __cplusplus > 201402L
+      using node_type = typename _Rep_type::node_type;
+#endif
+
       // [23.3.2] construct/copy/destroy
       // (get_allocator() is also listed in this section)
 
@@ -595,6 +602,57 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       { this->insert(__l.begin(), __l.end()); }
 #endif
 
+#if __cplusplus > 201402L
+      /// Extract a node.
+      node_type
+      extract(const_iterator __pos)
+      { return _M_t.extract(__pos); }
+
+      /// Extract a node.
+      node_type
+      extract(const key_type& __x)
+      { return _M_t.extract(__x); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(node_type&& __nh)
+      { return _M_t._M_reinsert_node_equal(std::move(__nh)); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      { return _M_t._M_reinsert_node_hint_equal(__hint, std::move(__nh)); }
+
+      template<typename, typename>
+       friend class _Rb_tree_merge_helper;
+
+      template<typename _C2>
+       void
+       merge(multimap<_Key, _Tp, _C2, _Alloc>& __source)
+       {
+         using _Merge_helper = _Rb_tree_merge_helper<multimap, _C2>;
+         _M_t._M_merge_equal(_Merge_helper::_S_get_tree(__source));
+       }
+
+      template<typename _C2>
+       void
+       merge(multimap<_Key, _Tp, _C2, _Alloc>&& __source)
+       { merge(__source); }
+
+      template<typename _C2>
+       void
+       merge(map<_Key, _Tp, _C2, _Alloc>& __source)
+       {
+         using _Merge_helper = _Rb_tree_merge_helper<multimap, _C2>;
+         _M_t._M_merge_equal(_Merge_helper::_S_get_tree(__source));
+       }
+
+      template<typename _C2>
+       void
+       merge(map<_Key, _Tp, _C2, _Alloc>&& __source)
+       { merge(__source); }
+#endif // C++17
+
 #if __cplusplus >= 201103L
       // _GLIBCXX_RESOLVE_LIB_DEFECTS
       // DR 130. Associative erase should return an iterator.
@@ -1030,6 +1088,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     { __x.swap(__y); }
 
 _GLIBCXX_END_NAMESPACE_CONTAINER
+
+#if __cplusplus > 201402L
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+  // Allow std::multimap access to internals of compatible maps.
+  template<typename _Key, typename _Val, typename _Cmp1, typename _Alloc,
+          typename _Cmp2>
+    struct
+    _Rb_tree_merge_helper<_GLIBCXX_STD_C::multimap<_Key, _Val, _Cmp1, _Alloc>,
+                         _Cmp2>
+    {
+    private:
+      friend class _GLIBCXX_STD_C::multimap<_Key, _Val, _Cmp1, _Alloc>;
+
+      static auto&
+      _S_get_tree(_GLIBCXX_STD_C::map<_Key, _Val, _Cmp2, _Alloc>& __map)
+      { return __map._M_t; }
+
+      static auto&
+      _S_get_tree(_GLIBCXX_STD_C::multimap<_Key, _Val, _Cmp2, _Alloc>& __map)
+      { return __map._M_t; }
+    };
+_GLIBCXX_END_NAMESPACE_VERSION
+#endif // C++17
+
 } // namespace std
 
 #endif /* _STL_MULTIMAP_H */
index 13b253c..d7312df 100644 (file)
@@ -65,6 +65,9 @@ namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
+  template<typename _Key, typename _Compare, typename _Alloc>
+    class set;
+
   /**
    *  @brief A standard container made up of elements, which can be retrieved
    *  in logarithmic time.
@@ -133,6 +136,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       typedef typename _Rep_type::size_type                 size_type;
       typedef typename _Rep_type::difference_type           difference_type;
 
+#if __cplusplus > 201402L
+      using node_type = typename _Rep_type::node_type;
+#endif
+
       // allocation/deallocation
       /**
        *  @brief  Default constructor creates no elements.
@@ -538,6 +545,57 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       { this->insert(__l.begin(), __l.end()); }
 #endif
 
+#if __cplusplus > 201402L
+      /// Extract a node.
+      node_type
+      extract(const_iterator __pos)
+      { return _M_t.extract(__pos); }
+
+      /// Extract a node.
+      node_type
+      extract(const key_type& __x)
+      { return _M_t.extract(__x); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(node_type&& __nh)
+      { return _M_t._M_reinsert_node_equal(std::move(__nh)); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      { return _M_t._M_reinsert_node_hint_equal(__hint, std::move(__nh)); }
+
+      template<typename, typename>
+       friend class _Rb_tree_merge_helper;
+
+      template<typename _Compare1>
+       void
+       merge(multiset<_Key, _Compare1, _Alloc>& __source)
+       {
+         using _Merge_helper = _Rb_tree_merge_helper<multiset, _Compare1>;
+         _M_t._M_merge_equal(_Merge_helper::_S_get_tree(__source));
+       }
+
+      template<typename _Compare1>
+       void
+       merge(multiset<_Key, _Compare1, _Alloc>&& __source)
+       { merge(__source); }
+
+      template<typename _Compare1>
+       void
+       merge(set<_Key, _Compare1, _Alloc>& __source)
+       {
+         using _Merge_helper = _Rb_tree_merge_helper<multiset, _Compare1>;
+         _M_t._M_merge_equal(_Merge_helper::_S_get_tree(__source));
+       }
+
+      template<typename _Compare1>
+       void
+       merge(set<_Key, _Compare1, _Alloc>&& __source)
+       { merge(__source); }
+#endif // C++17
+
 #if __cplusplus >= 201103L
       // _GLIBCXX_RESOLVE_LIB_DEFECTS
       // DR 130. Associative erase should return an iterator.
@@ -881,6 +939,29 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     { __x.swap(__y); }
 
 _GLIBCXX_END_NAMESPACE_CONTAINER
+
+#if __cplusplus > 201402L
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+  // Allow std::multiset access to internals of compatible sets.
+  template<typename _Val, typename _Cmp1, typename _Alloc, typename _Cmp2>
+    struct
+    _Rb_tree_merge_helper<_GLIBCXX_STD_C::multiset<_Val, _Cmp1, _Alloc>,
+                         _Cmp2>
+    {
+    private:
+      friend class _GLIBCXX_STD_C::multiset<_Val, _Cmp1, _Alloc>;
+
+      static auto&
+      _S_get_tree(_GLIBCXX_STD_C::set<_Val, _Cmp2, _Alloc>& __set)
+      { return __set._M_t; }
+
+      static auto&
+      _S_get_tree(_GLIBCXX_STD_C::multiset<_Val, _Cmp2, _Alloc>& __set)
+      { return __set._M_t; }
+    };
+_GLIBCXX_END_NAMESPACE_VERSION
+#endif // C++17
+
 } // namespace std
 
 #endif /* _STL_MULTISET_H */
index c104310..fd96dd4 100644 (file)
@@ -65,6 +65,9 @@ namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
+  template<typename _Key, typename _Compare, typename _Alloc>
+    class multiset;
+
   /**
    *  @brief A standard container made up of unique keys, which can be
    *  retrieved in logarithmic time.
@@ -135,6 +138,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       typedef typename _Rep_type::difference_type           difference_type;
       //@}
 
+#if __cplusplus > 201402L
+      using node_type = typename _Rep_type::node_type;
+      using insert_return_type = typename _Rep_type::insert_return_type;
+#endif
+
       // allocation/deallocation
       /**
        *  @brief  Default constructor creates no elements.
@@ -553,6 +561,57 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       { this->insert(__l.begin(), __l.end()); }
 #endif
 
+#if __cplusplus > 201402L
+      /// Extract a node.
+      node_type
+      extract(const_iterator __pos)
+      { return _M_t.extract(__pos); }
+
+      /// Extract a node.
+      node_type
+      extract(const key_type& __x)
+      { return _M_t.extract(__x); }
+
+      /// Re-insert an extracted node.
+      insert_return_type
+      insert(node_type&& __nh)
+      { return _M_t._M_reinsert_node_unique(std::move(__nh)); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      { return _M_t._M_reinsert_node_hint_unique(__hint, std::move(__nh)); }
+
+      template<typename, typename>
+       friend class _Rb_tree_merge_helper;
+
+      template<typename _Compare1>
+       void
+       merge(set<_Key, _Compare1, _Alloc>& __source)
+       {
+         using _Merge_helper = _Rb_tree_merge_helper<set, _Compare1>;
+         _M_t._M_merge_unique(_Merge_helper::_S_get_tree(__source));
+       }
+
+      template<typename _Compare1>
+       void
+       merge(set<_Key, _Compare1, _Alloc>&& __source)
+       { merge(__source); }
+
+      template<typename _Compare1>
+       void
+       merge(multiset<_Key, _Compare1, _Alloc>& __source)
+       {
+         using _Merge_helper = _Rb_tree_merge_helper<set, _Compare1>;
+         _M_t._M_merge_unique(_Merge_helper::_S_get_tree(__source));
+       }
+
+      template<typename _Compare1>
+       void
+       merge(multiset<_Key, _Compare1, _Alloc>&& __source)
+       { merge(__source); }
+#endif // C++17
+
 #if __cplusplus >= 201103L
       // _GLIBCXX_RESOLVE_LIB_DEFECTS
       // DR 130. Associative erase should return an iterator.
@@ -897,5 +956,27 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     { __x.swap(__y); }
 
 _GLIBCXX_END_NAMESPACE_CONTAINER
+
+#if __cplusplus > 201402L
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+  // Allow std::set access to internals of compatible sets.
+  template<typename _Val, typename _Cmp1, typename _Alloc, typename _Cmp2>
+    struct
+    _Rb_tree_merge_helper<_GLIBCXX_STD_C::set<_Val, _Cmp1, _Alloc>, _Cmp2>
+    {
+    private:
+      friend class _GLIBCXX_STD_C::set<_Val, _Cmp1, _Alloc>;
+
+      static auto&
+      _S_get_tree(_GLIBCXX_STD_C::set<_Val, _Cmp2, _Alloc>& __set)
+      { return __set._M_t; }
+
+      static auto&
+      _S_get_tree(_GLIBCXX_STD_C::multiset<_Val, _Cmp2, _Alloc>& __set)
+      { return __set._M_t; }
+    };
+_GLIBCXX_END_NAMESPACE_VERSION
+#endif // C++17
+
 } //namespace std
 #endif /* _STL_SET_H */
index 02b00b0..32da609 100644 (file)
 #include <bits/cpp_type_traits.h>
 #include <ext/alloc_traits.h>
 #if __cplusplus >= 201103L
-#include <ext/aligned_buffer.h>
+# include <ext/aligned_buffer.h>
+#endif
+#if __cplusplus > 201402L
+# include <bits/node_handle.h>
 #endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
@@ -356,6 +359,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { typedef void type; };
 #endif
 
+#if __cplusplus > 201402L
+  template<typename _Tree1, typename _Cmp2>
+    struct _Rb_tree_merge_helper { };
+#endif
+
   template<typename _Key, typename _Val, typename _KeyOfValue,
            typename _Compare, typename _Alloc = allocator<_Val> >
     class _Rb_tree
@@ -735,6 +743,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef std::reverse_iterator<iterator>       reverse_iterator;
       typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
 
+#if __cplusplus > 201402L
+      using node_type = _Node_handle<_Key, _Val, _Node_allocator>;
+      using insert_return_type = _Node_insert_return<iterator, node_type>;
+#endif
+
       pair<_Base_ptr, _Base_ptr>
       _M_get_insert_unique_pos(const key_type& __k);
 
@@ -1274,6 +1287,172 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       void
       _M_move_assign(_Rb_tree&, std::false_type);
 #endif
+
+#if __cplusplus > 201402L
+    public:
+      /// Re-insert an extracted node.
+      insert_return_type
+      _M_reinsert_node_unique(node_type&& __nh)
+      {
+       insert_return_type __ret;
+       if (__nh.empty())
+         __ret.position = end();
+       else
+         {
+           __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
+
+           auto __res = _M_get_insert_unique_pos(__nh._M_key());
+           if (__res.second)
+             {
+               __ret.position
+                 = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+               __nh._M_ptr = nullptr;
+               __ret.inserted = true;
+             }
+           else
+             {
+               __ret.node = std::move(__nh);
+               __ret.position = iterator(__res.first);
+               __ret.inserted = false;
+             }
+         }
+       return __ret;
+      }
+
+      /// Re-insert an extracted node.
+      iterator
+      _M_reinsert_node_equal(node_type&& __nh)
+      {
+       iterator __ret;
+       if (__nh.empty())
+         __ret = end();
+       else
+         {
+           __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
+           auto __res = _M_get_insert_equal_pos(__nh._M_key());
+           if (__res.second)
+             __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+           else
+             __ret = _M_insert_equal_lower_node(__nh._M_ptr);
+           __nh._M_ptr = nullptr;
+         }
+       return __ret;
+      }
+
+      /// Re-insert an extracted node.
+      iterator
+      _M_reinsert_node_hint_unique(const_iterator __hint, node_type&& __nh)
+      {
+       iterator __ret;
+       if (__nh.empty())
+         __ret = end();
+       else
+         {
+           __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
+           auto __res = _M_get_insert_hint_unique_pos(__hint, __nh._M_key());
+           if (__res.second)
+             {
+               __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+               __nh._M_ptr = nullptr;
+             }
+           else
+             __ret = iterator(__res.first);
+         }
+       return __ret;
+      }
+
+      /// Re-insert an extracted node.
+      iterator
+      _M_reinsert_node_hint_equal(const_iterator __hint, node_type&& __nh)
+      {
+       iterator __ret;
+       if (__nh.empty())
+         __ret = end();
+       else
+         {
+           __glibcxx_assert(_M_get_Node_allocator() == *__nh._M_alloc);
+           auto __res = _M_get_insert_hint_equal_pos(__hint, __nh._M_key());
+           if (__res.second)
+             __ret = _M_insert_node(__res.first, __res.second, __nh._M_ptr);
+           else
+             __ret = _M_insert_equal_lower_node(__nh._M_ptr);
+           __nh._M_ptr = nullptr;
+         }
+       return __ret;
+      }
+
+      /// Extract a node.
+      node_type
+      extract(const_iterator __pos)
+      {
+       auto __ptr = _Rb_tree_rebalance_for_erase(
+           __pos._M_const_cast()._M_node, _M_impl._M_header);
+       --_M_impl._M_node_count;
+       return { static_cast<_Link_type>(__ptr), _M_get_Node_allocator() };
+      }
+
+      /// Extract a node.
+      node_type
+      extract(const key_type& __k)
+      {
+       node_type __nh;
+       auto __pos = find(__k);
+       if (__pos != end())
+         __nh = extract(const_iterator(__pos));
+       return __nh;
+      }
+
+      template<typename _Compare2>
+       using _Compatible_tree
+         = _Rb_tree<_Key, _Val, _KeyOfValue, _Compare2, _Alloc>;
+
+      template<typename, typename>
+       friend class _Rb_tree_merge_helper;
+
+      /// Merge from a compatible container into one with unique keys.
+      template<typename _Compare2>
+       void
+       _M_merge_unique(_Compatible_tree<_Compare2>& __src) noexcept
+       {
+         using _Merge_helper = _Rb_tree_merge_helper<_Rb_tree, _Compare2>;
+         for (auto __i = __src.begin(), __end = __src.end(); __i != __end;)
+           {
+             auto __pos = __i++;
+             auto __res = _M_get_insert_unique_pos(_KeyOfValue()(*__pos));
+             if (__res.second)
+               {
+                 auto& __src_impl = _Merge_helper::_S_get_impl(__src);
+                 auto __ptr = _Rb_tree_rebalance_for_erase(
+                     __pos._M_node, __src_impl._M_header);
+                 --__src_impl._M_node_count;
+                 _M_insert_node(__res.first, __res.second,
+                                static_cast<_Link_type>(__ptr));
+               }
+           }
+       }
+
+      /// Merge from a compatible container into one with equivalent keys.
+      template<typename _Compare2>
+       void
+       _M_merge_equal(_Compatible_tree<_Compare2>& __src) noexcept
+       {
+         using _Merge_helper = _Rb_tree_merge_helper<_Rb_tree, _Compare2>;
+         for (auto __i = __src.begin(), __end = __src.end(); __i != __end;)
+           {
+             auto __pos = __i++;
+             auto __res = _M_get_insert_equal_pos(_KeyOfValue()(*__pos));
+             if (__res.second)
+               {
+                 auto& __src_impl = _Merge_helper::_S_get_impl(__src);
+                 auto __ptr = _Rb_tree_rebalance_for_erase(
+                     __pos._M_node, __src_impl._M_header);
+                 --__src_impl._M_node_count;
+                 _M_insert_node(__res.first, __res.second,
+                                static_cast<_Link_type>(__ptr));
+               }
+           }
+       }
+#endif // C++17
     };
 
   template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2390,6 +2569,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return true;
     }
 
+#if __cplusplus > 201402L
+  // Allow access to internals of compatible _Rb_tree specializations.
+  template<typename _Key, typename _Val, typename _Sel, typename _Cmp1,
+          typename _Alloc, typename _Cmp2>
+    struct _Rb_tree_merge_helper<_Rb_tree<_Key, _Val, _Sel, _Cmp1, _Alloc>,
+                                _Cmp2>
+    {
+    private:
+      friend class _Rb_tree<_Key, _Val, _Sel, _Cmp1, _Alloc>;
+
+      static auto&
+      _S_get_impl(_Rb_tree<_Key, _Val, _Sel, _Cmp2, _Alloc>& __tree)
+      { return __tree._M_impl; }
+    };
+#endif // C++17
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
index a26ee1a..ab8a762 100644 (file)
@@ -68,6 +68,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
                                         __detail::_Default_ranged_hash,
                                         __detail::_Prime_rehash_policy, _Tr>;
 
+  template<class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
+    class unordered_multimap;
+
   /**
    *  @brief A standard container composed of unique keys (containing
    *  at most one of each key value) that associates values of another type
@@ -126,6 +129,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       typedef typename _Hashtable::difference_type     difference_type;
       //@}
 
+#if __cplusplus > 201402L
+      using node_type = typename _Hashtable::node_type;
+      using insert_return_type = typename _Hashtable::insert_return_type;
+#endif
+
       //construct/destroy/copy
 
       /// Default constructor.
@@ -409,8 +417,27 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        emplace_hint(const_iterator __pos, _Args&&... __args)
        { return _M_h.emplace_hint(__pos, std::forward<_Args>(__args)...); }
 
-
 #if __cplusplus > 201402L
+      /// Extract a node.
+      node_type
+      extract(const_iterator __pos)
+      { return _M_h.extract(__pos); }
+
+      /// Extract a node.
+      node_type
+      extract(const key_type& __key)
+      { return _M_h.extract(__key); }
+
+      /// Re-insert an extracted node.
+      insert_return_type
+      insert(node_type&& __nh)
+      { return _M_h._M_reinsert_node(std::move(__nh)); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(const_iterator, node_type&& __nh)
+      { return _M_h._M_reinsert_node(std::move(__nh)).position; }
+
 #define __cpp_lib_unordered_map_try_emplace 201411
       /**
        *  @brief Attempts to build and insert a std::pair into the
@@ -524,7 +551,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
                                  std::forward<_Args>(__args)...));
           return __i;
         }
-#endif
+#endif // C++17
 
       //@{
       /**
@@ -817,6 +844,37 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       noexcept( noexcept(_M_h.swap(__x._M_h)) )
       { _M_h.swap(__x._M_h); }
 
+#if __cplusplus > 201402L
+      template<typename, typename, typename>
+       friend class _Hash_merge_helper;
+
+      template<typename _H2, typename _P2>
+       void
+       merge(unordered_map<_Key, _Tp, _H2, _P2, _Alloc>& __source)
+       {
+         using _Merge_helper = _Hash_merge_helper<unordered_map, _H2, _P2>;
+         _M_h._M_merge_unique(_Merge_helper::_S_get_table(__source));
+       }
+
+      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)
+       {
+         using _Merge_helper = _Hash_merge_helper<unordered_map, _H2, _P2>;
+         _M_h._M_merge_unique(_Merge_helper::_S_get_table(__source));
+       }
+
+      template<typename _H2, typename _P2>
+       void
+       merge(unordered_multimap<_Key, _Tp, _H2, _P2, _Alloc>&& __source)
+       { merge(__source); }
+#endif // C++17
+
       // observers.
 
       ///  Returns the hash functor object with which the %unordered_map was
@@ -1052,8 +1110,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       template<typename _Key1, typename _Tp1, typename _Hash1, typename _Pred1,
               typename _Alloc1>
         friend bool
-      operator==(const unordered_map<_Key1, _Tp1, _Hash1, _Pred1, _Alloc1>&,
-                const unordered_map<_Key1, _Tp1, _Hash1, _Pred1, _Alloc1>&);
+       operator==(const unordered_map<_Key1, _Tp1, _Hash1, _Pred1, _Alloc1>&,
+                  const unordered_map<_Key1, _Tp1, _Hash1, _Pred1, _Alloc1>&);
     };
 
   /**
@@ -1114,6 +1172,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       typedef typename _Hashtable::difference_type     difference_type;
       //@}
 
+#if __cplusplus > 201402L
+      using node_type = typename _Hashtable::node_type;
+#endif
+
       //construct/destroy/copy
 
       /// Default constructor.
@@ -1468,6 +1530,28 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       insert(initializer_list<value_type> __l)
       { _M_h.insert(__l); }
 
+#if __cplusplus > 201402L
+      /// Extract a node.
+      node_type
+      extract(const_iterator __pos)
+      { return _M_h.extract(__pos); }
+
+      /// Extract a node.
+      node_type
+      extract(const key_type& __key)
+      { return _M_h.extract(__key); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(node_type&& __nh)
+      { return _M_h._M_reinsert_node_multi(cend(), std::move(__nh)); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      { return _M_h._M_reinsert_node_multi(__hint, std::move(__nh)); }
+#endif // C++17
+
       //@{
       /**
        *  @brief Erases an element from an %unordered_multimap.
@@ -1551,6 +1635,39 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       noexcept( noexcept(_M_h.swap(__x._M_h)) )
       { _M_h.swap(__x._M_h); }
 
+#if __cplusplus > 201402L
+      template<typename, typename, typename>
+       friend class _Hash_merge_helper;
+
+      template<typename _H2, typename _P2>
+       void
+       merge(unordered_multimap<_Key, _Tp, _H2, _P2, _Alloc>& __source)
+       {
+         using _Merge_helper
+           = _Hash_merge_helper<unordered_multimap, _H2, _P2>;
+         _M_h._M_merge_multi(_Merge_helper::_S_get_table(__source));
+       }
+
+      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)
+       {
+         using _Merge_helper
+           = _Hash_merge_helper<unordered_multimap, _H2, _P2>;
+         _M_h._M_merge_multi(_Merge_helper::_S_get_table(__source));
+       }
+
+      template<typename _H2, typename _P2>
+       void
+       merge(unordered_map<_Key, _Tp, _H2, _P2, _Alloc>&& __source)
+       { merge(__source); }
+#endif // C++17
+
       // observers.
 
       ///  Returns the hash functor object with which the %unordered_multimap
@@ -1786,6 +1903,59 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     { return !(__x == __y); }
 
 _GLIBCXX_END_NAMESPACE_CONTAINER
+
+#if __cplusplus > 201402L
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+  // Allow std::unordered_map access to internals of compatible maps.
+  template<typename _Key, typename _Val, typename _Hash1, typename _Eq1,
+          typename _Alloc, typename _Hash2, typename _Eq2>
+    struct _Hash_merge_helper<
+      _GLIBCXX_STD_C::unordered_map<_Key, _Val, _Hash1, _Eq1, _Alloc>,
+      _Hash2, _Eq2>
+    {
+    private:
+      template<typename... _Tp>
+       using unordered_map = _GLIBCXX_STD_C::unordered_map<_Tp...>;
+      template<typename... _Tp>
+       using unordered_multimap = _GLIBCXX_STD_C::unordered_multimap<_Tp...>;
+
+      friend unordered_map<_Key, _Val, _Hash1, _Eq1, _Alloc>;
+
+      static auto&
+      _S_get_table(unordered_map<_Key, _Val, _Hash2, _Eq2, _Alloc>& __map)
+      { return __map._M_h; }
+
+      static auto&
+      _S_get_table(unordered_multimap<_Key, _Val, _Hash2, _Eq2, _Alloc>& __map)
+      { return __map._M_h; }
+    };
+
+  // Allow std::unordered_multimap access to internals of compatible maps.
+  template<typename _Key, typename _Val, typename _Hash1, typename _Eq1,
+          typename _Alloc, typename _Hash2, typename _Eq2>
+    struct _Hash_merge_helper<
+      _GLIBCXX_STD_C::unordered_multimap<_Key, _Val, _Hash1, _Eq1, _Alloc>,
+      _Hash2, _Eq2>
+    {
+    private:
+      template<typename... _Tp>
+       using unordered_map = _GLIBCXX_STD_C::unordered_map<_Tp...>;
+      template<typename... _Tp>
+       using unordered_multimap = _GLIBCXX_STD_C::unordered_multimap<_Tp...>;
+
+      friend unordered_multimap<_Key, _Val, _Hash1, _Eq1, _Alloc>;
+
+      static auto&
+      _S_get_table(unordered_map<_Key, _Val, _Hash2, _Eq2, _Alloc>& __map)
+      { return __map._M_h; }
+
+      static auto&
+      _S_get_table(unordered_multimap<_Key, _Val, _Hash2, _Eq2, _Alloc>& __map)
+      { return __map._M_h; }
+    };
+_GLIBCXX_END_NAMESPACE_VERSION
+#endif // C++17
+
 } // namespace std
 
 #endif /* _UNORDERED_MAP_H */
index 1e65b54..e5bb2be 100644 (file)
@@ -65,6 +65,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
                                         __detail::_Default_ranged_hash,
                                         __detail::_Prime_rehash_policy, _Tr>;
 
+  template<class _Value, class _Hash, class _Pred, class _Alloc>
+    class unordered_multiset;
+
   /**
    *  @brief A standard container composed of unique keys (containing
    *  at most one of each key value) in which the elements' keys are
@@ -120,6 +123,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       typedef typename _Hashtable::difference_type     difference_type;
       //@}
 
+#if __cplusplus > 201402L
+      using node_type = typename _Hashtable::node_type;
+      using insert_return_type = typename _Hashtable::insert_return_type;
+#endif
+
       // construct/destroy/copy
 
       /// Default constructor.
@@ -470,6 +478,28 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       insert(initializer_list<value_type> __l)
       { _M_h.insert(__l); }
 
+#if __cplusplus > 201402L
+      /// Extract a node.
+      node_type
+      extract(const_iterator __pos)
+      { return _M_h.extract(__pos); }
+
+      /// Extract a node.
+      node_type
+      extract(const key_type& __key)
+      { return _M_h.extract(__key); }
+
+      /// Re-insert an extracted node.
+      insert_return_type
+      insert(node_type&& __nh)
+      { return _M_h._M_reinsert_node(std::move(__nh)); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(const_iterator, node_type&& __nh)
+      { return _M_h._M_reinsert_node(std::move(__nh)).position; }
+#endif // C++17
+
       //@{
       /**
        *  @brief Erases an element from an %unordered_set.
@@ -552,6 +582,37 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       noexcept( noexcept(_M_h.swap(__x._M_h)) )
       { _M_h.swap(__x._M_h); }
 
+#if __cplusplus > 201402L
+      template<typename, typename, typename>
+       friend class _Hash_merge_helper;
+
+      template<typename _H2, typename _P2>
+       void
+       merge(unordered_set<_Value, _H2, _P2, _Alloc>& __source)
+       {
+         using _Merge_helper = _Hash_merge_helper<unordered_set, _H2, _P2>;
+         _M_h._M_merge_unique(_Merge_helper::_S_get_table(__source));
+       }
+
+      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)
+       {
+         using _Merge_helper = _Hash_merge_helper<unordered_set, _H2, _P2>;
+         _M_h._M_merge_unique(_Merge_helper::_S_get_table(__source));
+       }
+
+      template<typename _H2, typename _P2>
+       void
+       merge(unordered_multiset<_Value, _H2, _P2, _Alloc>&& __source)
+       { merge(__source); }
+#endif // C++17
+
       // observers.
 
       ///  Returns the hash functor object with which the %unordered_set was
@@ -793,6 +854,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       typedef typename _Hashtable::difference_type     difference_type;
       //@}
 
+#if __cplusplus > 201402L
+      using node_type = typename _Hashtable::node_type;
+#endif
+
       // construct/destroy/copy
 
       /// Default constructor.
@@ -1121,6 +1186,28 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       insert(initializer_list<value_type> __l)
       { _M_h.insert(__l); }
 
+#if __cplusplus > 201402L
+      /// Extract a node.
+      node_type
+      extract(const_iterator __pos)
+      { return _M_h.extract(__pos); }
+
+      /// Extract a node.
+      node_type
+      extract(const key_type& __key)
+      { return _M_h.extract(__key); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(node_type&& __nh)
+      { return _M_h._M_reinsert_node_multi(cend(), std::move(__nh)); }
+
+      /// Re-insert an extracted node.
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      { return _M_h._M_reinsert_node_multi(__hint, std::move(__nh)); }
+#endif // C++17
+
       //@{
       /**
        *  @brief Erases an element from an %unordered_multiset.
@@ -1208,6 +1295,39 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       noexcept( noexcept(_M_h.swap(__x._M_h)) )
       { _M_h.swap(__x._M_h); }
 
+#if __cplusplus > 201402L
+      template<typename, typename, typename>
+       friend class _Hash_merge_helper;
+
+      template<typename _H2, typename _P2>
+       void
+       merge(unordered_multiset<_Value, _H2, _P2, _Alloc>& __source)
+       {
+         using _Merge_helper
+           = _Hash_merge_helper<unordered_multiset, _H2, _P2>;
+         _M_h._M_merge_multi(_Merge_helper::_S_get_table(__source));
+       }
+
+      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)
+       {
+         using _Merge_helper
+           = _Hash_merge_helper<unordered_multiset, _H2, _P2>;
+         _M_h._M_merge_multi(_Merge_helper::_S_get_table(__source));
+       }
+
+      template<typename _H2, typename _P2>
+       void
+       merge(unordered_set<_Value, _H2, _P2, _Alloc>&& __source)
+       { merge(__source); }
+#endif // C++17
+
       // observers.
 
       ///  Returns the hash functor object with which the %unordered_multiset
@@ -1429,6 +1549,58 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     { return !(__x == __y); }
 
 _GLIBCXX_END_NAMESPACE_CONTAINER
+
+#if __cplusplus > 201402L
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+  // Allow std::unordered_set access to internals of compatible sets.
+  template<typename _Val, typename _Hash1, typename _Eq1, typename _Alloc,
+          typename _Hash2, typename _Eq2>
+    struct _Hash_merge_helper<
+      _GLIBCXX_STD_C::unordered_set<_Val, _Hash1, _Eq1, _Alloc>, _Hash2, _Eq2>
+    {
+    private:
+      template<typename... _Tp>
+       using unordered_set = _GLIBCXX_STD_C::unordered_set<_Tp...>;
+      template<typename... _Tp>
+       using unordered_multiset = _GLIBCXX_STD_C::unordered_multiset<_Tp...>;
+
+      friend unordered_set<_Val, _Hash1, _Eq1, _Alloc>;
+
+      static auto&
+      _S_get_table(unordered_set<_Val, _Hash2, _Eq2, _Alloc>& __set)
+      { return __set._M_h; }
+
+      static auto&
+      _S_get_table(unordered_multiset<_Val, _Hash2, _Eq2, _Alloc>& __set)
+      { return __set._M_h; }
+    };
+
+  // Allow std::unordered_multiset access to internals of compatible sets.
+  template<typename _Val, typename _Hash1, typename _Eq1, typename _Alloc,
+          typename _Hash2, typename _Eq2>
+    struct _Hash_merge_helper<
+      _GLIBCXX_STD_C::unordered_multiset<_Val, _Hash1, _Eq1, _Alloc>,
+      _Hash2, _Eq2>
+    {
+    private:
+      template<typename... _Tp>
+       using unordered_set = _GLIBCXX_STD_C::unordered_set<_Tp...>;
+      template<typename... _Tp>
+       using unordered_multiset = _GLIBCXX_STD_C::unordered_multiset<_Tp...>;
+
+      friend unordered_multiset<_Val, _Hash1, _Eq1, _Alloc>;
+
+      static auto&
+      _S_get_table(unordered_set<_Val, _Hash2, _Eq2, _Alloc>& __set)
+      { return __set._M_h; }
+
+      static auto&
+      _S_get_table(unordered_multiset<_Val, _Hash2, _Eq2, _Alloc>& __set)
+      { return __set._M_h; }
+    };
+_GLIBCXX_END_NAMESPACE_VERSION
+#endif // C++17
+
 } // namespace std
 
 #endif /* _UNORDERED_SET_H */
index 432bed3..51cd140 100644 (file)
@@ -397,8 +397,52 @@ namespace __debug
                                                  std::forward<_Obj>(__obj)),
                          this);
        }
-#endif
+#endif // C++17
+
+#if __cplusplus > 201402L
+      using node_type = typename _Base::node_type;
+
+      struct insert_return_type
+      {
+       bool inserted;
+       iterator position;
+       node_type node;
+      };
+
+      node_type
+      extract(const_iterator __position)
+      {
+       __glibcxx_check_erase(__position);
+       this->_M_invalidate_if(_Equal(__position.base()));
+       return _Base::extract(__position.base());
+      }
+
+      node_type
+      extract(const key_type& __key)
+      {
+       const auto __position = find(__key);
+       if (__position != end())
+         return extract(__position);
+       return {};
+      }
+
+      insert_return_type
+      insert(node_type&& __nh)
+      {
+       auto __ret = _Base::insert(std::move(__nh));
+       iterator __pos = iterator(__ret.position, this);
+       return { __ret.inserted, __pos, std::move(__ret.node) };
+      }
+
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      {
+       __glibcxx_check_insert(__hint);
+       return iterator(_Base::insert(__hint.base(), std::move(__nh)), this);
+      }
 
+      using _Base::merge;
+#endif // C++17
 
 #if __cplusplus >= 201103L
       iterator
index 30e7b19..0cecedb 100644 (file)
@@ -296,6 +296,40 @@ namespace __debug
            _Base::insert(__first, __last);
        }
 
+#if __cplusplus > 201402L
+      using node_type = typename _Base::node_type;
+
+      node_type
+      extract(const_iterator __position)
+      {
+       __glibcxx_check_erase(__position);
+       this->_M_invalidate_if(_Equal(__position.base()));
+       return _Base::extract(__position.base());
+      }
+
+      node_type
+      extract(const key_type& __key)
+      {
+       const auto __position = find(__key);
+       if (__position != end())
+         return extract(__position);
+       return {};
+      }
+
+      iterator
+      insert(node_type&& __nh)
+      { return iterator(_Base::insert(std::move(__nh)), this); }
+
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      {
+       __glibcxx_check_insert(__hint);
+       return iterator(_Base::insert(__hint.base(), std::move(__nh)), this);
+      }
+
+      using _Base::merge;
+#endif // C++17
+
 #if __cplusplus >= 201103L
       iterator
       erase(const_iterator __position)
index 67eb7e1..2d68a3d 100644 (file)
@@ -287,6 +287,40 @@ namespace __debug
       { _Base::insert(__l); }
 #endif
 
+#if __cplusplus > 201402L
+      using node_type = typename _Base::node_type;
+
+      node_type
+      extract(const_iterator __position)
+      {
+       __glibcxx_check_erase(__position);
+       this->_M_invalidate_if(_Equal(__position.base()));
+       return _Base::extract(__position.base());
+      }
+
+      node_type
+      extract(const key_type& __key)
+      {
+       const auto __position = find(__key);
+       if (__position != end())
+         return extract(__position);
+       return {};
+      }
+
+      iterator
+      insert(node_type&& __nh)
+      { return iterator(_Base::insert(std::move(__nh)), this); }
+
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      {
+       __glibcxx_check_insert(__hint);
+       return iterator(_Base::insert(__hint.base(), std::move(__nh)), this);
+      }
+
+      using _Base::merge;
+#endif // C++17
+
 #if __cplusplus >= 201103L
       iterator
       erase(const_iterator __position)
index ede4103..8937638 100644 (file)
@@ -296,6 +296,51 @@ namespace __debug
       { _Base::insert(__l); }
 #endif
 
+#if __cplusplus > 201402L
+      using node_type = typename _Base::node_type;
+
+      struct insert_return_type
+      {
+       bool inserted;
+       iterator position;
+       node_type node;
+      };
+
+      node_type
+      extract(const_iterator __position)
+      {
+       __glibcxx_check_erase(__position);
+       this->_M_invalidate_if(_Equal(__position.base()));
+       return _Base::extract(__position.base());
+      }
+
+      node_type
+      extract(const key_type& __key)
+      {
+       const auto __position = find(__key);
+       if (__position != end())
+         return extract(__position);
+       return {};
+      }
+
+      insert_return_type
+      insert(node_type&& __nh)
+      {
+       auto __ret = _Base::insert(std::move(__nh));
+       iterator __pos = iterator(__ret.position, this);
+       return { __ret.inserted, __pos, std::move(__ret.node) };
+      }
+
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      {
+       __glibcxx_check_insert(__hint);
+       return iterator(_Base::insert(__hint.base(), std::move(__nh)), this);
+      }
+
+      using _Base::merge;
+#endif // C++17
+
 #if __cplusplus >= 201103L
       iterator
       erase(const_iterator __position)
index 873f36a..4a834c6 100644 (file)
@@ -458,8 +458,59 @@ namespace __debug
                                                  std::forward<_Obj>(__obj)),
                          this);
        }
-#endif
+#endif // C++17
+
+#if __cplusplus > 201402L
+      using node_type = typename _Base::node_type;
+
+      struct insert_return_type
+      {
+       bool inserted;
+       iterator position;
+       node_type node;
+      };
 
+      node_type
+      extract(const_iterator __position)
+      {
+       __glibcxx_check_erase(__position);
+       _Base_const_iterator __victim = __position.base();
+       this->_M_invalidate_if(
+           [__victim](_Base_const_iterator __it) { return __it == __victim; }
+           );
+       this->_M_invalidate_local_if(
+           [__victim](_Base_const_local_iterator __it) {
+               return __it._M_curr() == __victim._M_cur;
+           });
+       return _Base::extract(__position.base());
+      }
+
+      node_type
+      extract(const key_type& __key)
+      {
+       const auto __position = find(__key);
+       if (__position != end())
+         return extract(__position);
+       return {};
+      }
+
+      insert_return_type
+      insert(node_type&& __nh)
+      {
+       auto __ret = _Base::insert(std::move(__nh));
+       iterator __pos = iterator(__ret.position, this);
+       return { __ret.inserted, __pos, std::move(__ret.node) };
+      }
+
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      {
+       __glibcxx_check_insert(__hint);
+       return iterator(_Base::insert(__hint.base(), std::move(__nh)), this);
+      }
+
+      using _Base::merge;
+#endif // C++17
 
       iterator
       find(const key_type& __key)
@@ -913,6 +964,47 @@ namespace __debug
          _M_check_rehashed(__bucket_count);
        }
 
+#if __cplusplus > 201402L
+      using node_type = typename _Base::node_type;
+
+      node_type
+      extract(const_iterator __position)
+      {
+       __glibcxx_check_erase(__position);
+       _Base_const_iterator __victim = __position.base();
+       this->_M_invalidate_if(
+           [__victim](_Base_const_iterator __it) { return __it == __victim; }
+           );
+       this->_M_invalidate_local_if(
+           [__victim](_Base_const_local_iterator __it) {
+               return __it._M_curr() == __victim._M_cur;
+           });
+       return _Base::extract(__position.base());
+      }
+
+      node_type
+      extract(const key_type& __key)
+      {
+       const auto __position = find(__key);
+       if (__position != end())
+         return extract(__position);
+       return {};
+      }
+
+      iterator
+      insert(node_type&& __nh)
+      { return iterator(_Base::insert(std::move(__nh)), this); }
+
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      {
+       __glibcxx_check_insert(__hint);
+       return iterator(_Base::insert(__hint.base(), std::move(__nh)), this);
+      }
+
+      using _Base::merge;
+#endif // C++17
+
       iterator
       find(const key_type& __key)
       { return iterator(_Base::find(__key), this); }
index 6a4dba6..f8587c0 100644 (file)
@@ -370,6 +370,58 @@ namespace __debug
          _M_check_rehashed(__bucket_count);
        }
 
+#if __cplusplus > 201402L
+      using node_type = typename _Base::node_type;
+
+      struct insert_return_type
+      {
+       bool inserted;
+       iterator position;
+       node_type node;
+      };
+
+      node_type
+      extract(const_iterator __position)
+      {
+       __glibcxx_check_erase(__position);
+       _Base_const_iterator __victim = __position.base();
+       this->_M_invalidate_if(
+           [__victim](_Base_const_iterator __it) { return __it == __victim; }
+           );
+       this->_M_invalidate_local_if(
+           [__victim](_Base_const_local_iterator __it) {
+               return __it._M_curr() == __victim._M_cur;
+           });
+       return _Base::extract(__position.base());
+      }
+
+      node_type
+      extract(const key_type& __key)
+      {
+       const auto __position = find(__key);
+       if (__position != end())
+         return extract(__position);
+       return {};
+      }
+
+      insert_return_type
+      insert(node_type&& __nh)
+      {
+       auto __ret = _Base::insert(std::move(__nh));
+       iterator __pos = iterator(__ret.position, this);
+       return { __ret.inserted, __pos, std::move(__ret.node) };
+      }
+
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      {
+       __glibcxx_check_insert(__hint);
+       return iterator(_Base::insert(__hint.base(), std::move(__nh)), this);
+      }
+
+      using _Base::merge;
+#endif // C++17
+
       iterator
       find(const key_type& __key)
       { return iterator(_Base::find(__key), this); }
@@ -821,6 +873,47 @@ namespace __debug
          _M_check_rehashed(__bucket_count);
        }
 
+#if __cplusplus > 201402L
+      using node_type = typename _Base::node_type;
+
+      node_type
+      extract(const_iterator __position)
+      {
+       __glibcxx_check_erase(__position);
+       _Base_const_iterator __victim = __position.base();
+       this->_M_invalidate_if(
+           [__victim](_Base_const_iterator __it) { return __it == __victim; }
+           );
+       this->_M_invalidate_local_if(
+           [__victim](_Base_const_local_iterator __it) {
+               return __it._M_curr() == __victim._M_cur;
+           });
+       return _Base::extract(__position.base());
+      }
+
+      node_type
+      extract(const key_type& __key)
+      {
+       const auto __position = find(__key);
+       if (__position != end())
+         return extract(__position);
+       return {};
+      }
+
+      iterator
+      insert(node_type&& __nh)
+      { return iterator(_Base::insert(std::move(__nh)), this); }
+
+      iterator
+      insert(const_iterator __hint, node_type&& __nh)
+      {
+       __glibcxx_check_insert(__hint);
+       return iterator(_Base::insert(__hint.base(), std::move(__nh)), this);
+      }
+
+      using _Base::merge;
+#endif // C++17
+
       iterator
       find(const key_type& __key)
       { return iterator(_Base::find(__key), this); }
index 18c6f6b..b5fc2a0 100644 (file)
@@ -130,6 +130,10 @@ class UniquePointerPrinter:
         return ('std::unique_ptr<%s> containing %s' % (str(v.type.target()),
                                                        str(v)))
 
+def get_value_from_aligned_membuf(buf, valtype):
+    """Returns the value held in a __gnu_cxx::__aligned_membuf."""
+    return buf['_M_storage'].address.cast(valtype.pointer()).dereference()
+
 def get_value_from_list_node(node):
     """Returns the value held in an _List_node<_Val>"""
     try:
@@ -139,9 +143,8 @@ def get_value_from_list_node(node):
             return node['_M_data']
         elif member == '_M_storage':
             # C++11 implementation, node stores value in __aligned_membuf
-            p = node['_M_storage']['_M_storage'].address
-            p = p.cast(node.type.template_argument(0).pointer())
-            return p.dereference()
+            valtype = node.type.template_argument(0)
+            return get_value_from_aligned_membuf(node['_M_storage'], valtype)
     except:
         pass
     raise ValueError("Unsupported implementation for %s" % str(node.type))
@@ -461,9 +464,8 @@ def get_value_from_Rb_tree_node(node):
             return node['_M_value_field']
         elif member == '_M_storage':
             # C++11 implementation, node stores value in __aligned_membuf
-            p = node['_M_storage']['_M_storage'].address
-            p = p.cast(node.type.template_argument(0).pointer())
-            return p.dereference()
+            valtype = node.type.template_argument(0)
+            return get_value_from_aligned_membuf(node['_M_storage'], valtype)
     except:
         pass
     raise ValueError("Unsupported implementation for %s" % str(node.type))
@@ -1017,6 +1019,48 @@ class StdVariantPrinter(SingleObjContainerPrinter):
             return "%s [index %d] containing %s" % (self.typename, self.index, self.visualizer.to_string())
         return "%s [index %d]" % (self.typename, self.index)
 
+class StdNodeHandlePrinter(SingleObjContainerPrinter):
+    "Print a container node handle"
+
+    def __init__(self, typename, val):
+        self.value_type = val.type.template_argument(1)
+        nodetype = val.type.template_argument(2).template_argument(0)
+        self.is_rb_tree_node = nodetype.name.startswith('std::_Rb_tree_node')
+        self.is_map_node = val.type.template_argument(0) != self.value_type
+        nodeptr = val['_M_ptr']
+        if nodeptr:
+            if self.is_rb_tree_node:
+                contained_value = get_value_from_Rb_tree_node(nodeptr.dereference())
+            else:
+                contained_value = get_value_from_aligned_membuf(nodeptr['_M_storage'],
+                                                                self.value_type)
+            visualizer = gdb.default_visualizer(contained_value)
+        else:
+            contained_value = None
+            visualizer = None
+        optalloc = val['_M_alloc']
+        self.alloc = optalloc['_M_payload'] if optalloc['_M_engaged'] else None
+        super(StdNodeHandlePrinter, self).__init__(contained_value, visualizer,
+                                                   'array')
+
+    def to_string(self):
+
+        desc = 'node handle for '
+        if not self.is_rb_tree_node:
+            desc += 'unordered '
+        if self.is_map_node:
+            desc += 'map';
+        else:
+            desc += 'set';
+
+        if self.contained_value:
+            desc += ' with element'
+            if hasattr(self.visualizer, 'children'):
+                return "%s = %s" % (desc, self.visualizer.to_string())
+            return desc
+        else:
+            return 'empty %s' % desc
+
 class StdExpStringViewPrinter:
     "Print a std::basic_string_view or std::experimental::basic_string_view"
 
@@ -1491,6 +1535,8 @@ def build_libstdcxx_dictionary ():
                                   'basic_string_view', StdExpStringViewPrinter)
     libstdcxx_printer.add_version('std::',
                                   'variant', StdVariantPrinter)
+    libstdcxx_printer.add_version('std::',
+                                  '_Node_handle', StdNodeHandlePrinter)
 
     # Extensions.
     libstdcxx_printer.add_version('__gnu_cxx::', 'slist', StdSlistPrinter)
diff --git a/libstdc++-v3/testsuite/23_containers/map/modifiers/extract.cc b/libstdc++-v3/testsuite/23_containers/map/modifiers/extract.cc
new file mode 100644 (file)
index 0000000..dbbcc80
--- /dev/null
@@ -0,0 +1,147 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <map>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::map<int, int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ {1, 10}, {2, 20}, {3, 30} };
+  test_type::node_type node;
+  test_type::insert_return_type ins;
+  test_type::iterator pos;
+
+  node = c.extract(0);
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+
+  ins = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( !ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position == c.end() );
+
+  node = c.extract(1);
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 2 );
+  VERIFY( c.count(1) == 0 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.key() == 1 );
+  VERIFY( node.mapped() == 10 );
+
+  node.key() = 4;
+  node.mapped() = 40;
+  VERIFY( node.key() == 4 );
+  VERIFY( node.mapped() == 40 );
+
+  ins = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position != c.end() );
+  VERIFY( ins.position->first == 4 );
+  VERIFY( ins.position->second == 40 );
+  VERIFY( c.count(1) == 0 );
+  VERIFY( c.count(4) == 1 );
+  VERIFY( std::is_sorted(c.begin(), c.end()) );
+
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( pos == c.end() );
+
+  node = c.extract(2);
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( c.size() == 3 );
+  VERIFY( pos != c.end() );
+  VERIFY( pos->first == 2 );
+  VERIFY( pos->second == 20 );
+
+  test_type c2 = c;
+  node = c2.extract(3);
+  ins = c.insert(std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( ins.position != c.end() );
+  VERIFY( !ins.inserted );
+  VERIFY( !ins.node.empty() );
+  VERIFY( ins.node.key() == 3 );
+  VERIFY( ins.node.mapped() == 30 );
+  VERIFY( ins.position->first == ins.node.key() );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ {1, 10}, {2, 20}, {3, 30} };
+  test_type::node_type node;
+  test_type::insert_return_type ins;
+
+  node = c.extract(c.begin());
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 2 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.key() == 1 );
+  VERIFY( node.mapped() == 10 );
+
+  ins = c.insert(std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position != c.end() );
+  VERIFY( ins.position->first == 1 );
+  VERIFY( ins.position->second == 10 );
+}
+
+void
+test03()
+{
+  struct less : std::less<int> { };
+  using std::is_same_v;
+  using compat_type1 = std::map<int, int, less>;
+  static_assert( is_same_v<test_type::node_type, compat_type1::node_type> );
+  using compat_type2 = std::multimap<int, int>;
+  static_assert( is_same_v<test_type::node_type, compat_type2::node_type> );
+  using compat_type3 = std::multimap<int, int, less>;
+  static_assert( is_same_v<test_type::node_type, compat_type3::node_type> );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/map/modifiers/merge.cc b/libstdc++-v3/testsuite/23_containers/map/modifiers/merge.cc
new file mode 100644 (file)
index 0000000..c978ed2
--- /dev/null
@@ -0,0 +1,147 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <map>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::map<int, int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {2, 20}, {3, 30} };
+  test_type c1 = c0, c2 = c0;
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2 == c0 );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(std::move(c1));
+  VERIFY( c1.empty() );
+  VERIFY( c2 == c0 );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {2, 20}, {3, 30} };
+  test_type c1 = c0;
+  std::map<int, int, std::less<>> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( std::equal(c2.begin(), c2.end(), c0.begin(), c0.end()) );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(std::move(c1));
+  VERIFY( c1.empty() );
+  VERIFY( std::equal(c2.begin(), c2.end(), c0.begin(), c0.end()) );
+}
+
+void
+test03()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {2, 20}, {3, 30} };
+  test_type c1 = c0;
+  std::map<int, int, std::greater<>> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test04()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {2, 20}, {3, 30} };
+  test_type c1 = c0;
+  std::multimap<int, int, std::greater<>> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1 = c0;
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( c2.size() == (2 * c0.size()) );
+  VERIFY( std::is_sorted(c2.begin(), c2.end(), c2.value_comp()) );
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1.clear();
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multimap/modifiers/extract.cc b/libstdc++-v3/testsuite/23_containers/multimap/modifiers/extract.cc
new file mode 100644 (file)
index 0000000..cad131c
--- /dev/null
@@ -0,0 +1,141 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <map>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::multimap<int, int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ {1, 10}, { 1, 11 }, {2, 20}, { 2, 21}, {3, 30}, { 3, 31 } };
+  test_type::node_type node;
+  test_type::iterator pos;
+
+  node = c.extract(0);
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+
+  pos = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos == c.end() );
+
+  node = c.extract(1);
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 5 );
+  VERIFY( c.count(1) == 1 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.key() == 1 );
+  int mapped = node.mapped();
+  VERIFY( mapped == 10 || mapped == 11 );
+
+  node.key() = 4;
+  node.mapped() = 40;
+  VERIFY( node.key() == 4 );
+  VERIFY( node.mapped() == 40 );
+
+  pos = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( pos->first == 4 );
+  VERIFY( pos->second == 40 );
+  VERIFY( c.count(1) == 1 );
+  VERIFY( c.count(4) == 1 );
+  VERIFY( std::is_sorted(c.begin(), c.end()) );
+
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos == c.end() );
+
+  node = c.extract(1);
+  mapped = node.mapped();
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( pos->first == 1 );
+  VERIFY( pos->second == mapped );
+
+  test_type c2 = c;
+  node = c2.extract(1);
+  mapped = node.mapped();
+  pos = c.insert(std::move(node));
+  VERIFY( pos != c.end() );
+  VERIFY( node.empty() );
+  VERIFY( pos->first == 1 );
+  VERIFY( pos->second == mapped );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ {1, 10}, { 1, 11 }, {2, 20}, { 2, 21}, {3, 30}, { 3, 31 } };
+  test_type::node_type node;
+  test_type::iterator pos;
+
+  node = c.extract(c.begin());
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 5 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.key() == 1 );
+  VERIFY( node.mapped() == 10 );
+
+  pos = c.insert(std::next(c.begin()), std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( pos->first == 1 );
+  VERIFY( pos->second == 10 );
+  VERIFY( pos == std::next(c.begin()) );
+}
+
+void
+test03()
+{
+  struct less : std::less<int> { };
+  using std::is_same_v;
+  using compat_type1 = std::multimap<int, int, less>;
+  static_assert( is_same_v<test_type::node_type, compat_type1::node_type> );
+  using compat_type2 = std::map<int, int>;
+  static_assert( is_same_v<test_type::node_type, compat_type2::node_type> );
+  using compat_type3 = std::map<int, int, less>;
+  static_assert( is_same_v<test_type::node_type, compat_type3::node_type> );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multimap/modifiers/merge.cc b/libstdc++-v3/testsuite/23_containers/multimap/modifiers/merge.cc
new file mode 100644 (file)
index 0000000..70541ff
--- /dev/null
@@ -0,0 +1,119 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <map>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::multimap<int, int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {1, 11}, {2, 20}, {2, 21}, {3, 30}, {3, 31} };
+  test_type c1 = c0, c2 = c0;
+
+  c1.merge(c2);
+  for (auto& i : c1)
+    VERIFY( c1.count(i.first) == (2 * c0.count(i.first)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2 = c0;
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {1, 11}, {2, 20}, {2, 21}, {3, 30}, {3, 31} };
+  test_type c1 = c0;
+  std::multimap<int, int, std::less<>> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (2 * c0.size()) );
+  for (auto& i : c1)
+    VERIFY( c1.count(i.first) == (2 * c0.count(i.first)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test03()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {1, 11}, {2, 20}, {2, 21}, {3, 30}, {3, 31} };
+  test_type c1 = c0;
+  std::multimap<int, int, std::greater<>> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (2 * c0.size()) );
+  for (auto& i : c1)
+    VERIFY( c1.count(i.first) == (2 * c0.count(i.first)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test04()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {1, 11}, {2, 20}, {2, 21}, {3, 30}, {3, 31} };
+  test_type c1 = c0;
+  std::map<int, int, std::greater<>> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (1.5 * c0.size()) );
+  for (auto& i : c1)
+    VERIFY( c1.count(i.first) == (1.5 * c0.count(i.first)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1.size() == (0.5 * c0.size()) );
+  VERIFY( c2.empty() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multiset/modifiers/extract.cc b/libstdc++-v3/testsuite/23_containers/multiset/modifiers/extract.cc
new file mode 100644 (file)
index 0000000..56c2a28
--- /dev/null
@@ -0,0 +1,129 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <set>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::multiset<int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ 1, 1, 2, 2, 3, 3 };
+  test_type::node_type node;
+  test_type::iterator pos;
+
+  node = c.extract(0);
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+
+  pos = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos == c.end() );
+
+  node = c.extract(1);
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 5 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.value() == 1 );
+
+  node.value() = 4;
+  VERIFY( node.value() == 4 );
+
+  pos = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( *pos == 4 );
+  VERIFY( c.count(1) == 1 );
+  VERIFY( c.count(4) == 1 );
+  VERIFY( std::is_sorted(c.begin(), c.end()) );
+
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos == c.end() );
+
+  node = c.extract(1);
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( *pos == 1 );
+
+  test_type c2 = c;
+  node = c2.extract(1);
+  pos = c.insert(std::move(node));
+  VERIFY( pos != c.end() );
+  VERIFY( node.empty() );
+  VERIFY( *pos == 1 );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ 1, 1, 2, 2, 3, 3 };
+  test_type::node_type node;
+  test_type::iterator pos;
+
+  node = c.extract(c.begin());
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 5 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.value() == 1 );
+
+  pos = c.insert(std::next(c.begin()), std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( *pos == 1 );
+  VERIFY( pos == std::next(c.begin()) );
+}
+
+void
+test03()
+{
+  struct less : std::less<int> { };
+  using std::is_same_v;
+  using compat_type1 = std::multiset<int, less>;
+  static_assert( is_same_v<test_type::node_type, compat_type1::node_type> );
+  using compat_type2 = std::set<int>;
+  static_assert( is_same_v<test_type::node_type, compat_type2::node_type> );
+  using compat_type3 = std::set<int, less>;
+  static_assert( is_same_v<test_type::node_type, compat_type3::node_type> );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/multiset/modifiers/merge.cc b/libstdc++-v3/testsuite/23_containers/multiset/modifiers/merge.cc
new file mode 100644 (file)
index 0000000..ba422b8
--- /dev/null
@@ -0,0 +1,117 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <set>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::multiset<int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c0{ 1, 1, 2, 2, 3, 3 };
+  test_type c1 = c0, c2 = c0;
+
+  c1.merge(c2);
+  for (auto i : c1)
+    VERIFY( c1.count(i) == (2 * c0.count(i)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2 = c0;
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c0{ 1, 1, 2, 2, 3, 3 };
+  test_type c1 = c0;
+  std::multiset<int, std::less<>> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (2 * c0.size()) );
+  for (auto i : c1)
+    VERIFY( c1.count(i) == (2 * c0.count(i)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test03()
+{
+  const test_type c0{ 1, 1, 2, 2, 3, 3 };
+  test_type c1 = c0;
+  std::multiset<int, std::greater<>> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (2 * c0.size()) );
+  for (auto i : c1)
+    VERIFY( c1.count(i) == (2 * c0.count(i)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test04()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ 1, 1, 2, 2, 3, 3 };
+  test_type c1 = c0;
+  std::set<int, std::greater<>> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (1.5 * c0.size()) );
+  for (auto& i : c1)
+    VERIFY( c1.count(i) == (1.5 * c0.count(i)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1.size() == (0.5 * c0.size()) );
+  VERIFY( c2.empty() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/set/modifiers/extract.cc b/libstdc++-v3/testsuite/23_containers/set/modifiers/extract.cc
new file mode 100644 (file)
index 0000000..db5872a
--- /dev/null
@@ -0,0 +1,138 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <set>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::set<int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ 1, 2, 3 };
+  test_type::node_type node;
+  test_type::insert_return_type ins;
+  test_type::iterator pos;
+
+  node = c.extract(0);
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+
+  ins = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( !ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position == c.end() );
+
+  node = c.extract(1);
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 2 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.value() == 1 );
+
+  node.value() = 4;
+  VERIFY( node.value() == 4 );
+
+  ins = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position != c.end() );
+  VERIFY( *ins.position == 4 );
+  VERIFY( c.count(1) == 0 );
+  VERIFY( c.count(4) == 1 );
+  VERIFY( std::is_sorted(c.begin(), c.end()) );
+
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( pos == c.end() );
+
+  node = c.extract(2);
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( c.size() == 3 );
+  VERIFY( pos != c.end() );
+  VERIFY( *pos == 2 );
+
+  test_type c2 = c;
+  node = c2.extract(3);
+  ins = c.insert(std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( ins.position != c.end() );
+  VERIFY( !ins.inserted );
+  VERIFY( !ins.node.empty() );
+  VERIFY( ins.node.value() == 3 );
+  VERIFY( *ins.position == ins.node.value() );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ 1, 2, 3 };
+  test_type::node_type node;
+  test_type::insert_return_type ins;
+
+  node = c.extract(c.begin());
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 2 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.value() == 1 );
+
+  ins = c.insert(std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position != c.end() );
+  VERIFY( *ins.position == 1 );
+}
+
+void
+test03()
+{
+  struct less : std::less<int> { };
+  using std::is_same_v;
+  using compat_type1 = std::set<int, less>;
+  static_assert( is_same_v<test_type::node_type, compat_type1::node_type> );
+  using compat_type2 = std::multiset<int>;
+  static_assert( is_same_v<test_type::node_type, compat_type2::node_type> );
+  using compat_type3 = std::multiset<int, less>;
+  static_assert( is_same_v<test_type::node_type, compat_type3::node_type> );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/set/modifiers/merge.cc b/libstdc++-v3/testsuite/23_containers/set/modifiers/merge.cc
new file mode 100644 (file)
index 0000000..b1e0693
--- /dev/null
@@ -0,0 +1,143 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <set>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::set<int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ 1, 2, 3 };
+  test_type c1 = c0, c2 = c0;
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2 == c0 );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(std::move(c1));
+  VERIFY( c1.empty() );
+  VERIFY( c2 == c0 );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ 1, 2, 3 };
+  test_type c1 = c0;
+  std::set<int, std::less<>> c2( c0.begin(), c0.end() );
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( std::equal(c2.begin(), c2.end(), c0.begin(), c0.end()) );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(std::move(c1));
+  VERIFY( c1.empty() );
+  VERIFY( std::equal(c2.begin(), c2.end(), c0.begin(), c0.end()) );
+}
+
+void
+test03()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ 1, 2, 3 };
+  test_type c1 = c0;
+  std::set<int, std::greater<>> c2( c0.begin(), c0.end() );
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test04()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ 1, 2, 3 };
+  test_type c1 = c0;
+  std::multiset<int, std::greater<>> c2( c0.begin(), c0.end() );
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1 = c0;
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( c2.size() == (2 * c0.size()) );
+  VERIFY( std::is_sorted(c2.begin(), c2.end(), c2.value_comp()) );
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( std::equal(c2.rbegin(), c2.rend(), c0.begin(), c0.end()) );
+
+  c1.clear();
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/modifiers/extract.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/modifiers/extract.cc
new file mode 100644 (file)
index 0000000..9d9c43e
--- /dev/null
@@ -0,0 +1,148 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <unordered_map>
+#include <testsuite_hooks.h>
+
+using test_type = std::unordered_map<int, int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ {1, 10}, {2, 20}, {3, 30} };
+  test_type::node_type node;
+  test_type::insert_return_type ins;
+  test_type::iterator pos;
+
+  node = c.extract(0);
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+
+  ins = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( !ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position == c.end() );
+
+  node = c.extract(1);
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 2 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.key() == 1 );
+  VERIFY( node.mapped() == 10 );
+
+  node.key() = 4;
+  node.mapped() = 40;
+  VERIFY( node.key() == 4 );
+  VERIFY( node.mapped() == 40 );
+
+  ins = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position != c.end() );
+  VERIFY( ins.position->first == 4 );
+  VERIFY( ins.position->second == 40 );
+  VERIFY( c.count(1) == 0 );
+  VERIFY( c.count(4) == 1 );
+
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( pos == c.end() );
+
+  pos = c.insert(c.begin(), c.extract(2));
+  VERIFY( c.size() == 3 );
+  VERIFY( pos != c.end() );
+  VERIFY( pos->first == 2 );
+  VERIFY( pos->second == 20 );
+
+  test_type c2 = c;
+  node = c2.extract(3);
+  ins = c.insert(std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( ins.position != c.end() );
+  VERIFY( !ins.inserted );
+  VERIFY( !ins.node.empty() );
+  VERIFY( ins.node.key() == 3 );
+  VERIFY( ins.node.mapped() == 30 );
+  auto hasher = c.hash_function();
+  VERIFY( hasher(ins.position->first) == hasher(ins.node.key()) );
+  auto eq = c.key_eq();
+  VERIFY( eq(ins.position->first, ins.node.key()) );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ {1, 10}, {2, 20}, {3, 30} };
+  test_type::node_type node;
+  test_type::insert_return_type ins;
+
+  const int key = c.begin()->first;
+  node = c.extract(c.begin());
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 2 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.key() == key );
+  VERIFY( node.mapped() == (key * 10) );
+
+  ins = c.insert(std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position != c.end() );
+  VERIFY( ins.position->first == key );
+  VERIFY( ins.position->second == (key * 10) );
+}
+
+void
+test03()
+{
+  struct hash : std::hash<int> { };
+  struct equal : std::equal_to<int> { };
+  using std::is_same_v;
+  using compat_type1 = std::unordered_map<int, int, hash, equal>;
+  static_assert( is_same_v<test_type::node_type, compat_type1::node_type> );
+  using compat_type2 = std::unordered_multimap<int, int>;
+  static_assert( is_same_v<test_type::node_type, compat_type2::node_type> );
+  using compat_type3 = std::unordered_multimap<int, int, hash, equal>;
+  static_assert( is_same_v<test_type::node_type, compat_type3::node_type> );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/modifiers/merge.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/modifiers/merge.cc
new file mode 100644 (file)
index 0000000..f0035d2
--- /dev/null
@@ -0,0 +1,140 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <unordered_map>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::unordered_map<int, int>;
+
+struct hash {
+  auto operator()(int i) const noexcept { return ~std::hash<int>()(i); }
+};
+struct equal : std::equal_to<> { };
+
+template<typename C1, typename C2>
+bool equal_elements(const C1& c1, const C2& c2)
+{
+  if (c2.size() != c1.size())
+    return false;
+  for (auto& i : c1)
+    if (c2.count(i.first) != c1.count(i.first))
+      return false;
+  return true;
+}
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {2, 20}, {3, 30} };
+  test_type c1 = c0, c2 = c0;
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2 == c0 );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(std::move(c1));
+  VERIFY( c1.empty() );
+  VERIFY( c2 == c0 );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {2, 20}, {3, 30} };
+  test_type c1 = c0;
+  std::unordered_map<int, int, hash, equal> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1.merge(std::move(c2));
+  VERIFY( c2.empty() );
+  VERIFY( c1 == c0 );
+}
+
+void
+test03()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {2, 20}, {3, 30} };
+  test_type c1 = c0;
+  std::unordered_multimap<int, int, hash, equal> c2( c0.begin(), c0.end() );
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1 = c0;
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( c2.size() == (2 * c0.size()) );
+  VERIFY( c2.count(1) == 2 );
+  VERIFY( c2.count(2) == 2 );
+  VERIFY( c2.count(3) == 2 );
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1.clear();
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/modifiers/extract.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/modifiers/extract.cc
new file mode 100644 (file)
index 0000000..635e7d0
--- /dev/null
@@ -0,0 +1,138 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <unordered_map>
+#include <testsuite_hooks.h>
+
+using test_type = std::unordered_multimap<int, int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ {1, 10}, { 1, 11 }, {2, 20}, { 2, 21}, {3, 30}, { 3, 31 } };
+  test_type::node_type node;
+  test_type::iterator pos;
+
+  node = c.extract(0);
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+
+  pos = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos == c.end() );
+
+  node = c.extract(1);
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 5 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.key() == 1 );
+  int mapped = node.mapped();
+  VERIFY( mapped == 10 || mapped == 11 );
+
+  node.key() = 4;
+  node.mapped() = 40;
+  VERIFY( node.key() == 4 );
+  VERIFY( node.mapped() == 40 );
+
+  pos = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( pos->first == 4 );
+  VERIFY( pos->second == 40 );
+
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos == c.end() );
+
+  node = c.extract(1);
+  mapped = node.mapped();
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( pos->first == 1 );
+  VERIFY( pos->second == mapped );
+
+  test_type c2 = c;
+  node = c2.extract(1);
+  mapped = node.mapped();
+  pos = c.insert(std::move(node));
+  VERIFY( pos != c.end() );
+  VERIFY( node.empty() );
+  VERIFY( pos->first == 1 );
+  VERIFY( pos->second == mapped );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ {1, 10}, { 1, 11 }, {2, 20}, { 2, 21}, {3, 30}, { 3, 31 } };
+  test_type::node_type node;
+  test_type::iterator pos;
+
+  const int key = c.begin()->first;
+  const int mapped = c.begin()->second;
+  node = c.extract(c.begin());
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 5 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.key() == key );
+  VERIFY( node.mapped() == mapped );
+
+  pos = c.insert(std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( pos->first == key );
+  VERIFY( pos->second == mapped );
+}
+
+void
+test03()
+{
+  struct hash : std::hash<int> { };
+  struct equal : std::equal_to<int> { };
+  using std::is_same_v;
+  using compat_type1 = std::unordered_multimap<int, int, hash, equal>;
+  static_assert( is_same_v<test_type::node_type, compat_type1::node_type> );
+  using compat_type2 = std::unordered_map<int, int>;
+  static_assert( is_same_v<test_type::node_type, compat_type2::node_type> );
+  using compat_type3 = std::unordered_map<int, int, hash, equal>;
+  static_assert( is_same_v<test_type::node_type, compat_type3::node_type> );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/modifiers/merge.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/modifiers/merge.cc
new file mode 100644 (file)
index 0000000..3c67e3c
--- /dev/null
@@ -0,0 +1,123 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <unordered_map>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::unordered_multimap<int, int>;
+struct hash {
+  auto operator()(int i) const noexcept { return ~std::hash<int>()(i); }
+};
+struct equal : std::equal_to<> { };
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {1, 11}, {2, 20}, {2, 21}, {3, 30}, {3, 31} };
+  test_type c1 = c0, c2 = c0;
+
+  c1.merge(c2);
+  for (auto& i : c1)
+    VERIFY( c1.count(i.first) == (2 * c0.count(i.first)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2 = c0;
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {1, 11}, {2, 20}, {2, 21}, {3, 30}, {3, 31} };
+  test_type c1 = c0;
+  std::unordered_multimap<int, int, hash, equal> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (2 * c0.size()) );
+  for (auto& i : c1)
+    VERIFY( c1.count(i.first) == (2 * c0.count(i.first)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test03()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {1, 11}, {2, 20}, {2, 21}, {3, 30}, {3, 31} };
+  test_type c1 = c0;
+  std::unordered_multimap<int, int, hash, equal> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (2 * c0.size()) );
+  for (auto& i : c1)
+    VERIFY( c1.count(i.first) == (2 * c0.count(i.first)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test04()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ {1, 10}, {1, 11}, {2, 20}, {2, 21}, {3, 30}, {3, 31} };
+  test_type c1 = c0;
+  std::unordered_map<int, int, hash, equal> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (1.5 * c0.size()) );
+  for (auto& i : c1)
+    VERIFY( c1.count(i.first) == (1.5 * c0.count(i.first)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1.size() == (0.5 * c0.size()) );
+  VERIFY( c2.empty() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/modifiers/extract.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/modifiers/extract.cc
new file mode 100644 (file)
index 0000000..e703dc4
--- /dev/null
@@ -0,0 +1,128 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <unordered_set>
+#include <testsuite_hooks.h>
+
+using test_type = std::unordered_multiset<int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ 1, 1, 2, 2, 3, 3 };
+  test_type::node_type node;
+  test_type::iterator pos;
+
+  node = c.extract(0);
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+
+  pos = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos == c.end() );
+
+  node = c.extract(1);
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 5 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.value() == 1 );
+
+  node.value() = 4;
+  VERIFY( node.value() == 4 );
+
+  pos = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( *pos == 4 );
+  VERIFY( c.count(1) == 1 );
+  VERIFY( c.count(4) == 1 );
+
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos == c.end() );
+
+  node = c.extract(1);
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( *pos == 1 );
+
+  test_type c2 = c;
+  node = c2.extract(1);
+  pos = c.insert(std::move(node));
+  VERIFY( pos != c.end() );
+  VERIFY( node.empty() );
+  VERIFY( *pos == 1 );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ 1, 1, 2, 2, 3, 3 };
+  test_type::node_type node;
+  test_type::iterator pos;
+
+  const int val = *c.begin();
+  node = c.extract(c.begin());
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 5 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.value() == val );
+
+  pos = c.insert(std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 6 );
+  VERIFY( pos != c.end() );
+  VERIFY( *pos == val );
+}
+
+void
+test03()
+{
+  struct hash : std::hash<int> { };
+  struct equal : std::equal_to<int> { };
+  using std::is_same_v;
+  using compat_type1 = std::unordered_multiset<int, hash, equal>;
+  static_assert( is_same_v<test_type::node_type, compat_type1::node_type> );
+  using compat_type2 = std::unordered_set<int>;
+  static_assert( is_same_v<test_type::node_type, compat_type2::node_type> );
+  using compat_type3 = std::unordered_set<int, hash, equal>;
+  static_assert( is_same_v<test_type::node_type, compat_type3::node_type> );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/modifiers/merge.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/modifiers/merge.cc
new file mode 100644 (file)
index 0000000..4ce4b84
--- /dev/null
@@ -0,0 +1,121 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <unordered_set>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::unordered_multiset<int>;
+struct hash {
+  auto operator()(int i) const noexcept { return ~std::hash<int>()(i); }
+};
+struct equal : std::equal_to<> { };
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c0{ 1, 1, 2, 2, 3, 3 };
+  test_type c1 = c0, c2 = c0;
+
+  c1.merge(c2);
+  for (auto i : c1)
+    VERIFY( c1.count(i) == (2 * c0.count(i)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2 = c0;
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c0{ 1, 1, 2, 2, 3, 3 };
+  test_type c1 = c0;
+  std::unordered_multiset<int, hash, equal> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (2 * c0.size()) );
+  for (auto i : c1)
+    VERIFY( c1.count(i) == (2 * c0.count(i)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test03()
+{
+  const test_type c0{ 1, 1, 2, 2, 3, 3 };
+  test_type c1 = c0;
+  std::unordered_multiset<int, hash, equal> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (2 * c0.size()) );
+  for (auto i : c1)
+    VERIFY( c1.count(i) == (2 * c0.count(i)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+void
+test04()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ 1, 1, 2, 2, 3, 3 };
+  test_type c1 = c0;
+  std::unordered_set<int, hash, equal> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1.size() == (1.5 * c0.size()) );
+  for (auto& i : c1)
+    VERIFY( c1.count(i) == (1.5 * c0.count(i)) );
+  VERIFY( c2.empty() );
+
+  c1.clear();
+  c2.insert( c0.begin(), c0.end() );
+  c1.merge(std::move(c2));
+  VERIFY( c1.size() == (0.5 * c0.size()) );
+  VERIFY( c2.empty() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
index cc572fc..5e8de37 100644 (file)
@@ -18,7 +18,7 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// { dg-error "with noexcept" "" { target *-*-* } 265 }
+// { dg-error "with noexcept" "" { target *-*-* } 268 }
 
 #include <unordered_set>
 
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/extract.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/extract.cc
new file mode 100644 (file)
index 0000000..cb48cbc
--- /dev/null
@@ -0,0 +1,140 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <unordered_set>
+#include <testsuite_hooks.h>
+
+using test_type = std::unordered_set<int>;
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ 1, 2, 3 };
+  test_type::node_type node;
+  test_type::insert_return_type ins;
+  test_type::iterator pos;
+
+  node = c.extract(0);
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+
+  ins = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( !ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position == c.end() );
+
+  node = c.extract(1);
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 2 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.value() == 1 );
+
+  node.value() = 4;
+  VERIFY( node.value() == 4 );
+
+  ins = c.insert(std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position != c.end() );
+  VERIFY( *ins.position == 4 );
+  VERIFY( c.count(1) == 0 );
+  VERIFY( c.count(4) == 1 );
+
+  pos = c.insert(c.begin(), std::move(node));
+  VERIFY( !node );
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( pos == c.end() );
+
+  pos = c.insert(c.begin(), c.extract(2));
+  VERIFY( c.size() == 3 );
+  VERIFY( pos != c.end() );
+  VERIFY( *pos == 2 );
+
+  test_type c2 = c;
+  node = c2.extract(3);
+  ins = c.insert(std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( ins.position != c.end() );
+  VERIFY( !ins.inserted );
+  VERIFY( !ins.node.empty() );
+  VERIFY( ins.node.value() == 3 );
+  auto hasher = c.hash_function();
+  VERIFY( hasher(*ins.position) == hasher(ins.node.value()) );
+  auto eq = c.key_eq();
+  VERIFY( eq(*ins.position, ins.node.value()) );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  test_type c{ 1, 2, 3 };
+  test_type::node_type node;
+  test_type::insert_return_type ins;
+
+  const int val = *c.begin();
+  node = c.extract(c.begin());
+  VERIFY( (bool)node );
+  VERIFY( !node.empty() );
+  VERIFY( c.size() == 2 );
+  VERIFY( node.get_allocator() == c.get_allocator() );
+  VERIFY( node.value() == val );
+
+  ins = c.insert(std::move(node));
+  VERIFY( node.empty() );
+  VERIFY( c.size() == 3 );
+  VERIFY( ins.inserted );
+  VERIFY( !ins.node );
+  VERIFY( ins.position != c.end() );
+  VERIFY( *ins.position == val );
+}
+
+void
+test03()
+{
+  struct hash : std::hash<int> { };
+  struct equal : std::equal_to<int> { };
+  using std::is_same_v;
+  using compat_type1 = std::unordered_set<int, hash, equal>;
+  static_assert( is_same_v<test_type::node_type, compat_type1::node_type> );
+  using compat_type2 = std::unordered_multiset<int>;
+  static_assert( is_same_v<test_type::node_type, compat_type2::node_type> );
+  using compat_type3 = std::unordered_multiset<int, hash, equal>;
+  static_assert( is_same_v<test_type::node_type, compat_type3::node_type> );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/merge.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/merge.cc
new file mode 100644 (file)
index 0000000..29387dd
--- /dev/null
@@ -0,0 +1,140 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+
+#include <unordered_set>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+using test_type = std::unordered_set<int>;
+
+struct hash {
+  auto operator()(int i) const noexcept { return ~std::hash<int>()(i); }
+};
+struct equal : std::equal_to<> { };
+
+template<typename C1, typename C2>
+bool equal_elements(const C1& c1, const C2& c2)
+{
+  if (c2.size() != c1.size())
+    return false;
+  for (auto& i : c1)
+    if (c2.count(i) != c1.count(i))
+      return false;
+  return true;
+}
+
+void
+test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ 1, 2, 3, };
+  test_type c1 = c0, c2 = c0;
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2 == c0 );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(std::move(c1));
+  VERIFY( c1.empty() );
+  VERIFY( c2 == c0 );
+}
+
+void
+test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ 1, 2, 3, };
+  test_type c1 = c0;
+  std::unordered_set<int, hash, equal> c2( c0.begin(), c0.end() );
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1.merge(std::move(c2));
+  VERIFY( c2.empty() );
+  VERIFY( c1 == c0 );
+}
+
+void
+test03()
+{
+  bool test __attribute__((unused)) = true;
+
+  const test_type c0{ 1, 2, 3, };
+  test_type c1 = c0;
+  std::unordered_multiset<int, hash, equal> c2( c0.begin(), c0.end() );
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1.clear();
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1 = c0;
+  c2.merge(c1);
+  VERIFY( c1.empty() );
+  VERIFY( c2.size() == (2 * c0.size()) );
+  VERIFY( c2.count(1) == 2 );
+  VERIFY( c2.count(2) == 2 );
+  VERIFY( c2.count(3) == 2 );
+
+  c1.merge(c2);
+  VERIFY( c1 == c0 );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( equal_elements(c2, c0) );
+
+  c1.clear();
+  c1.merge(std::move(c2));
+  VERIFY( c1 == c0 );
+  VERIFY( c2.empty() );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
index 09d79e9..bc9b26a 100644 (file)
 #include <string_view>
 #include <string>
 #include <map>
+#include <unordered_set>
 #include <iostream>
 
 using std::any;
 using std::optional;
 using std::variant;
 using std::string_view;
+using std::map;
+using std::unordered_set;
 
 int
 main()
@@ -83,10 +86,21 @@ main()
 // { dg-final { note-test v3 {std::variant<float, int, std::string_view> [index 1] = {3}} } }
   variant<float, int, string_view> v4{ str };
 // { dg-final { note-test v4 {std::variant<float, int, std::string_view> [index 2] = {"string"}} } }
-
   variant<string_view&> vref{str};
 // { dg-final { note-test vref {std::variant<std::basic_string_view<char, std::char_traits<char> > &> [index 0] = {"string"}} } }
 
+  map<int, string_view> m{ {1, "one"} };
+  map<int, string_view>::node_type n0;
+// { dg-final { note-test n0 {empty node handle for map}}}
+  map<int, string_view>::node_type n1 = m.extract(1);
+// { dg-final { note-test n1 {node handle for map with element = {{first = 1, second = "two"}}}}}
+
+  unordered_set<int> s{ 3, 4 };
+  unordered_set<int>::node_type n2;
+// { dg-final { note-test n2 {empty node handle for unordered set}}}
+  unordered_set<int>::node_type n3 = s.extract(3);
+// { dg-final { note-test n1 {node handle for unordered set with element = {3}}}}
+
   std::cout << "\n";
   return 0;                    // Mark SPOT
 }