<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 839: Maps and sets missing splice operation</title>
<meta property="og:title" content="Issue 839: Maps and sets missing splice operation">
<meta property="og:description" content="C++ library issue. Status: Resolved">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue839.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#Resolved">Resolved</a> status.</em></p>
<h3 id="839"><a href="lwg-defects.html#839">839</a>. Maps and sets missing splice operation</h3>
<p><b>Section:</b> 23.4 <a href="https://wg21.link/associative">[associative]</a>, 23.5 <a href="https://wg21.link/unord">[unord]</a> <b>Status:</b> <a href="lwg-active.html#Resolved">Resolved</a>
 <b>Submitter:</b> Alan Talbot <b>Opened:</b> 2008-05-18 <b>Last modified:</b> 2020-09-06</p>
<p><b>Priority: </b>Not Prioritized
</p>
<p><b>View all other</b> <a href="lwg-index.html#associative">issues</a> in [associative].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#Resolved">Resolved</a> status.</p>
<p><b>Discussion:</b></p>
<p>
Splice is a very useful feature of <code>list</code>. This functionality is also very
useful for any other node based container, and I frequently wish it were
available for maps and sets. It seems like an omission that these
containers lack this capability. Although the complexity for a splice is
the same as for an insert, the actual time can be much less since the
objects need not be reallocated and copied. When the element objects are
heavy and the compare operations are fast (say a <code>map&lt;int, huge_thingy&gt;</code>)
this can be a big win.
</p>

<p>
<b>Suggested resolution:</b>
</p>

<p>
Add the following signatures to map, set, multimap, multiset, and the unordered associative containers:
</p>
<blockquote><pre> 
void splice(list&lt;T,Allocator&gt;&amp;&amp; x);
void splice(list&lt;T,Allocator&gt;&amp;&amp; x, const_iterator i);
void splice(list&lt;T,Allocator&gt;&amp;&amp; x, const_iterator first, const_iterator last);
</pre></blockquote>

<p>
Hint versions of these are also useful to the extent hint is useful.
(I'm looking for guidance about whether hints are in fact useful.)
</p>
 
<blockquote><pre> 
void splice(const_iterator position, list&lt;T,Allocator&gt;&amp;&amp; x);
void splice(const_iterator position, list&lt;T,Allocator&gt;&amp;&amp; x, const_iterator i);
void splice(const_iterator position, list&lt;T,Allocator&gt;&amp;&amp; x, const_iterator first, const_iterator last);
</pre></blockquote>

<p><i>[
Sophia Antipolis:
]</i></p>


<blockquote>
<p>
Don't try to <code>splice "list"</code> into the other containers, it should be container-type.
</p>
<p>
<code>forward_list</code> already has <code>splice_after</code>.
</p>
<p>
Would "<code>splice</code>" make sense for an <code>unordered_map</code>?
</p>
<p>
Jens, Robert: "<code>splice</code>" is not the right term, it implies maintaining ordering in <code>list</code>s.
</p>
<p>
Howard: <code>adopt</code>?
</p>
<p>
Jens: <code>absorb</code>?
</p>
<p>
Alan: <code>subsume</code>?
</p>
<p>
Robert: <code>recycle</code>?
</p>
<p>
Howard: <code>transfer</code>? (but no direction)
</p>
<p>
Jens: <code>transfer_from</code>. No.
</p>
<p>
Alisdair: Can we give a nothrow guarantee? If your <code>compare()</code> and <code>hash()</code> doesn't throw, yes.
</p>
<p>
Daniel: For <code>unordered_map</code>, we can't guarantee nothrow.
</p>
</blockquote>

<p><i>[
San Francisco:
]</i></p>


<blockquote>
<p>
Martin: this would possibly outlaw an implementation technique that is
currently in use; caching nodes in containers.
</p>
<p>
Alan: if you cache in the allocator, rather than the individual
container, this proposal doesn't interfere with that.
</p>
<p>
Martin: I'm not opposed to this, but I'd like to see an implementation
that demonstrates that it works.
</p>
</blockquote>

<p><i>[
2009-07 Frankfurt:
]</i></p>


<blockquote><p>
NAD Future.
</p></blockquote>

<p><i>[
2009-09-19 Howard adds:
]</i></p>


<blockquote>
<p>
I'm not disagreeing with the NAD Future resolution.  But when the future gets
here, here is a possibility worth exploring:
</p>

<blockquote>
<p>
Add to the "unique" associative containers:
</p>

<blockquote><pre>
typedef <i>details</i>      node_ptr;

node_ptr             remove(const_iterator p);
pair&lt;iterator, bool&gt; insert(node_ptr&amp;&amp; nd);
iterator             insert(const_iterator p, node_ptr&amp;&amp; nd);
</pre></blockquote>

<p>
And add to the "multi" associative containers:
</p>

<blockquote><pre>
typedef <i>details</i> node_ptr;

node_ptr remove(const_iterator p);
iterator insert(node_ptr&amp;&amp; nd);
iterator insert(const_iterator p, node_ptr&amp;&amp; nd);
</pre></blockquote>

<p>
<code>Container::node_ptr</code> is a smart pointer much like <code>unique_ptr</code>.
It owns a node obtained from the container it was removed from.  It maintains a
reference to the allocator in the container so that it can properly deallocate
the node if asked to, even if the allocator is stateful.  This being said, the
<code>node_ptr</code> can not outlive the container for this reason.
</p>

<p>
The <code>node_ptr</code> offers "<code>const</code>-free" access to the node's
<code>value_type</code>.
</p>

<p>
With this interface, clients have a great deal of flexibility:
</p>

<ul>
<li>
A client can remove a node from one container, and insert it into another
(without any heap allocation).  This is the splice functionality this issue
asks for.
</li>
<li>
A client can remove a node from a container, change its key or value, and insert
it back into the same container, or another container, all without the cost of
allocating a node.
</li>
<li>
If the Compare function is nothrow (which is very common), then this functionality
is nothrow unless modifying the value throws.  And if this does throw, it does
so outside of the containers involved.
</li>
<li>
If the Compare function does throw, the <code>insert</code> function will have the
argument <code>nd</code> retain ownership of the node.
</li>
<li>
The <code>node_ptr</code> should be independent of the <code>Compare</code> parameter
so that a node can be transferred from <code>set&lt;T, C1, A&gt;</code>
to <code>set&lt;T, C2, A&gt;</code> (for example).
</li>
</ul>

<p>
Here is how the customer might use this functionality:
</p>

<ul>
<li>
<p>
Splice a node from one container to another:
</p>
<blockquote><pre>
m2.insert(m1.remove(i));
</pre></blockquote>
</li>

<li>
<p>
Change the "key" in a <code>std::map</code> without the cost of node reallocation:
</p>
<blockquote><pre>
auto p = m.remove(i);
p->first = new_key;
m.insert(std::move(p));
</pre></blockquote>
</li>

<li>
<p>
Change the "value" in a <code>std::set</code> without the cost of node reallocation:
</p>
<blockquote><pre>
auto p = s.remove(i);
*p = new_value;
s.insert(std::move(p));
</pre></blockquote>
</li>

<li>
<p>
Move a move-only or heavy object out of an associative container (as opposed to
the proposal in <a href="lwg-defects.html#1041" title="Add associative/unordered container functions that allow to extract elements (Status: Resolved)">1041</a><sup><a href="https://cplusplus.github.io/LWG/issue1041" title="Latest snapshot">(i)</a></sup>):
</p>
<blockquote><pre>
MoveOnly x = std::move(*s.remove(i));
</pre></blockquote>
<ol>
<li>
<code>remove(i)</code> transfers ownership of the node from the set to a temporary
<code>node_ptr</code>.
</li>
<li>
The <code>node_ptr</code> is dereferenced, and that non-const reference is sent to
<code>move</code> to cast it to an rvalue.
</li>
<li>
The rvalue <code>MoveOnly</code> is move constructed into <code>x</code> from
the <code>node_ptr</code>.
</li>
<li>
<code>~node_ptr()</code> destructs the moved-from <code>MoveOnly</code> and deallocates
the node.
</li>
</ol>

<p>
Contrast this with the <a href="lwg-defects.html#1041" title="Add associative/unordered container functions that allow to extract elements (Status: Resolved)">1041</a><sup><a href="https://cplusplus.github.io/LWG/issue1041" title="Latest snapshot">(i)</a></sup> solution:
</p>
<blockquote><pre>
MoveOnly x = std::move(s.extract(i).first);
</pre></blockquote>

<p>
The former requires one move construction for <code>x</code> while the latter
requires two (one into the <code>pair</code> and then one into <code>x</code>).  Either
of these constructions can throw (say if there is only a copy constructor for
<code>x</code>).  With the former, the point of throw is outside of the container
<code>s</code>, after the element has been removed from the container.  With the latter,
one throwing construction takes place prior to the removal of the element, and
the second takes place after the element is removed.
</p>

</li>
</ul>

<p>
The "node insertion" API maintains the API associated with inserting <code>value_type</code>s
so the customer can use familiar techniques for getting an iterator to the 
inserted node, or finding out whether it was inserted or not for the "unique"
containers.
</p>

<p>
Lightly prototyped.  No implementation problems.  Appears to work great
for the client.
</p>

</blockquote>
</blockquote>

<p><i>[08-2016, Post-Chicago]</i></p>

<p>Move to Tentatively Resolved</p>


<p id="res-839"><b>Proposed resolution:</b></p>
<p>This functionality is provided by <a href="https://wg21.link/p0083r3">P0083R3</a></p>





</body>
</html>
