implemented merge operations (#49)

* initial draft

* modified appveyor.yml for this branch

* dropped BOOST_RV_REF
as it doesn't handle lvalue refs

* befriended index_base from all indices
so as to be grant access to final_extract_for_merge_ from an external container

* added rvalue ref merge
(test expected to fail in C++03)

* added explicit boost::move on rvalue ref
for the benefit of C++03 compilers

* fixed previous workaround

* refactored merge algorithm into multi_index_container

* added hashed_index::merge

* reimplemented sequenced_index::splice(position,x)

* added missing this->'s

* refactored SFINAEing code

* made sequenced_index::splice(position,x) exception robust

* reimplemented random_access_index::splice(position,x)

* micro-optimized legacy code in random_access_index::splice(position,x)

* added missing #include

* reimplemented sequenced_index::splice(position,x,i)

* reimplemented random_access_index::splice(position,x,i)

* reimplemented sequenced_index::splice(position,x,first,last)

* reimplemented random_access_index::splice(position,x,first,last)

* re-engineered sequenced_index::splice(position,x,i)
so that it works when source and destination belong to the same container

* stylistic

* re-engineered random_access_index::splice(position,x,i)
so that it works when source and destination belong to the same container

* re-engineered sequenced_index::splice(position,x,first,last)
so that it works when source and destination belong to the same container

* stylistic

* fixed bug in rvalue ref overload of sequenced_index::splice(position,x,first,last)

* fixed internal sequenced_index::splice(position,x,first,last) across different indices

* re-engineered random_access_index::splice(position,x,first,last)
the same way as done with sequenced_index

* replaced multi_index_container::merge_ with transfer_range_
so as to refactor some code in non-key-based indices

* fixed safe mode check for different-type containers

* hardened merge/splice tests

* s/BOOST_RV_REF/BOOST_FWD_REF

* called the right merge_different overload

* fixed problem with Boost.Move C++03 perfect fwd emulation

* updated (C) year

* allowed intra-container full splice

* required position==end() in intra-container full splice

* made pointwise splice return a pair<iterator,bool>

* added partial merge functionality to key-based indices
plus fixed a compile-time error with legacy splice code when applied to different-type indices

* suppressed unused variable

* fixed wrong signature in splice memfun

* made safe-mode iterators SCARY

* refactored any_container_view

* implemented safe iterator transfer in merge/splice

* reorganized internal deps

* stylistic

* automatically checked iterator invalidation upon merge/splice

* removed commented-out code

* checked allocator equality

* reimplemented random_access_index internal, cross-index splice in linear time

* updated docs

* reverted appveyor.yml
This commit is contained in:
joaquintides 2021-08-19 10:41:03 +02:00 committed by GitHub
parent 2a2282b270
commit a52810fc3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 2300 additions and 557 deletions

View File

@ -1,4 +1,4 @@
# Copyright 2003-2021 Joaquín M López Muñoz. # Copyright 2003-2021 Joaquín M López Muñoz.
# Distributed under the Boost Software License, Version 1.0. # Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at # (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt) # http://www.boost.org/LICENSE_1_0.txt)
@ -34,4 +34,4 @@ build: off
test_script: test_script:
- if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD% - if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
- b2 address-model=32 libs/multi_index/test toolset=%TOOLSET% %CXXSTD% - b2 address-model=32 libs/multi_index/test toolset=%TOOLSET% %CXXSTD%

View File

@ -308,6 +308,16 @@ requirements.
<span class=keyword>void</span> <span class=identifier>clear</span><span class=special>()</span><span class=keyword>noexcept</span><span class=special>;</span> <span class=keyword>void</span> <span class=identifier>clear</span><span class=special>()</span><span class=keyword>noexcept</span><span class=special>;</span>
<span class=keyword>void</span> <span class=identifier>swap</span><span class=special>(</span><b>index class name</b><span class=special>&amp;</span> <span class=identifier>x</span><span class=special>);</span> <span class=keyword>void</span> <span class=identifier>swap</span><span class=special>(</span><b>index class name</b><span class=special>&amp;</span> <span class=identifier>x</span><span class=special>);</span>
<span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span> <span class=keyword>void</span> <span class=identifier>merge</span><span class=special>(</span><span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>);</span>
<span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span>
<span class=identifier>std</span><span class=special>::</span><span class=identifier>pair</span><span class=special>&lt;</span><span class=identifier>iterator</span><span class=special>,</span><span class=keyword>bool</span><span class=special>&gt;</span> <span class=identifier>merge</span><span class=special>(</span>
<span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>,</span><span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>i</span><span class=special>);</span>
<span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span>
<span class=keyword>void</span> <span class=identifier>merge</span><span class=special>(</span>
<span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>,</span>
<span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>first</span><span class=special>,</span>
<span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>last</span><span class=special>);</span>
<span class=comment>// observers:</span> <span class=comment>// observers:</span>
<span class=identifier>key_from_value</span> <span class=identifier>key_extractor</span><span class=special>()</span><span class=keyword>const</span><span class=special>;</span> <span class=identifier>key_from_value</span> <span class=identifier>key_extractor</span><span class=special>()</span><span class=keyword>const</span><span class=special>;</span>
@ -474,7 +484,9 @@ local_iterator<br>
const_local_iterator</code> const_local_iterator</code>
<blockquote> <blockquote>
These types are forward iterators. These types are forward iterators. They depend only on <code>node_type</code> and whether
the index is unique or not (this implies that, for instance, iterators to elements transferred
from a unique index to a non-unique one will become invalid).
</blockquote> </blockquote>
<h4><a name="constructors">Constructors, copy and assignment</a></h4> <h4><a name="constructors">Constructors, copy and assignment</a></h4>
@ -925,6 +937,89 @@ with <code>mod'</code> and <code>back</code> defined in such a way that
<code>key</code> is the internal <code>KeyFromValue</code> object of the index. <code>key</code> is the internal <code>KeyFromValue</code> object of the index.
</blockquote> </blockquote>
<code>template&lt;typename Index&gt; void merge(Index&amp;&amp; x);</code>
<blockquote>
<b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<a href="multi_index_container.html#node_type">node-compatible</a>
<code>multi_index_container</code>. <code>get_allocator()==x.get_allocator()</code>.<br>
<b>Effects:</b>
<blockquote><pre>
<span class=identifier>merge</span><span class=special>(</span><span class=identifier>x</span><span class=special>,</span><span class=identifier>x</span><span class=special>.</span><span class=identifier>begin</span><span class=special>(),</span><span class=identifier>x</span><span class=special>.</span><span class=identifier>end</span><span class=special>());</span>
</pre></blockquote>
</blockquote>
<code>
template&lt;typename Index&gt; std::pair&lt;iterator,bool&gt; merge(<br>
&nbsp;&nbsp;Index&amp;&amp; x,typename std::remove_reference_t&lt;Index&gt;::const_iterator i);
</code>
<blockquote>
<b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<a href="multi_index_container.html#node_type">node-compatible</a>
<code>multi_index_container</code>. <code>get_allocator()==x.get_allocator()</code>.
<code>i</code> is a valid dereferenceable iterator of <code>x</code>.<br>
<b>Effects:</b> Does nothing if the source and destination containers are the same;
otherwise, transfers the node of the element referred to by <code>i</code> into the
<code>multi_index_container</code> to which the destination index belongs if
<ul>
<li>the index is non-unique OR no other element exists with
equivalent key,</li>
<li>AND insertion is allowed by all other indices of the
<code>multi_index_container</code>.</li>
</ul>
Note that no element is copied or destroyed in the process.<br>
<b>Postconditions:</b> If transfer succeeds, for any index in the source container
having the same <code>iterator</code>/<code>const_iterator</code> types as the corresponding
index in the destination container, iterators referring to <code>*i</code>
remain valid and behave as iterators of the destination index.<br>
<b>Returns:</b> The return value is a pair <code>p</code>. <code>p.second</code>
is <code>true</code> if and only if transfer took place or the source and destination
containers are the same. If <code>p.second</code> is <code>true</code>,
<code>p.first</code> points to <code>*i</code>; otherwise, <code>p.first</code>
points to an element that caused the insertion to be banned. Note that more than
one element can be causing insertion not to be allowed.<br>
<b>Complexity:</b> If the source and destination containers are the same,
constant; otherwise, <code>O(I(n)+D(x.size()))</code>.<br>
<b>Exception safety:</b> If the source and destination containers are the same,
<code>nothrow</code>; otherwise strong.
</blockquote>
<code>
template&lt;typename Index&gt; void merge(<br>
&nbsp;&nbsp;Index&amp;&amp; x,<br>
&nbsp;&nbsp;typename std::remove_reference_t&lt;Index&gt;::const_iterator first,<br>
&nbsp;&nbsp;typename std::remove_reference_t&lt;Index&gt;::const_iterator last);
</code>
<blockquote>
<b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<a href="multi_index_container.html#node_type">node-compatible</a>
<code>multi_index_container</code>. <code>get_allocator()==x.get_allocator()</code>.
[<code>first</code>,<code>last</code>) is a valid range of <code>x</code>.<br>
<b>Effects:</b> Does nothing if the source and destination containers are the same;
otherwise, for each node in [<code>first</code>,<code>last</code>), in this order,
the node is transferred to the <code>multi_index_container</code> to which the
destination index belongs if
<ul>
<li>the index is non-unique OR no other element exists with
equivalent key,</li>
<li>AND insertion is allowed by all other indices of the
<code>multi_index_container</code>.</li>
</ul>
Note that no element is copied or destroyed in the process.<br>
<b>Postconditions:</b> For any index in the source container having the same
<code>iterator</code>/<code>const_iterator</code> types as the corresponding
index in the destination container, iterators referring to the transferred elements
remain valid and behave as iterators of the destination index.<br>
<b>Complexity:</b> If the source and destination containers are the same,
constant; otherwise, <code>O(m*(I(n+m)+D(x.size())))</code>, where
<code>m</code> is the number of elements in [<code>first</code>,
<code>last</code>).<br>
<b>Exception safety:</b> If the source and destination containers are the same,
<code>nothrow</code>; otherwise basic.
</blockquote>
<h4><a name="observers">Observers</a></h4> <h4><a name="observers">Observers</a></h4>
<p>Apart from standard <code>hash_function</code> and <code>key_eq</code>, <p>Apart from standard <code>hash_function</code> and <code>key_eq</code>,
@ -1275,7 +1370,7 @@ Sequenced indices
<br> <br>
<p>Revised July 29th 2021</p> <p>Revised August 16th 2021</p>
<p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz. <p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz.
Distributed under the Boost Software Distributed under the Boost Software

View File

@ -42,6 +42,7 @@ Ranked indices
<ul> <ul>
<li><a href="#complexity_signature">Complexity signature</a></li> <li><a href="#complexity_signature">Complexity signature</a></li>
<li><a href="#instantiation_types">Instantiation types</a></li> <li><a href="#instantiation_types">Instantiation types</a></li>
<li><a href="#types">Nested types</a></li>
<li><a href="#constructors">Constructors, copy and assignment</a></li> <li><a href="#constructors">Constructors, copy and assignment</a></li>
<li><a href="#iterators">Iterators</a></li> <li><a href="#iterators">Iterators</a></li>
<li><a href="#modifiers">Modifiers</a></li> <li><a href="#modifiers">Modifiers</a></li>
@ -310,6 +311,16 @@ do not exactly conform to or are not mandated by the standard requirements.
<span class=keyword>void</span> <span class=identifier>swap</span><span class=special>(</span><b>index class name</b><span class=special>&amp;</span> <span class=identifier>x</span><span class=special>);</span> <span class=keyword>void</span> <span class=identifier>swap</span><span class=special>(</span><b>index class name</b><span class=special>&amp;</span> <span class=identifier>x</span><span class=special>);</span>
<span class=keyword>void</span> <span class=identifier>clear</span><span class=special>()</span><span class=keyword>noexcept</span><span class=special>;</span> <span class=keyword>void</span> <span class=identifier>clear</span><span class=special>()</span><span class=keyword>noexcept</span><span class=special>;</span>
<span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span> <span class=keyword>void</span> <span class=identifier>merge</span><span class=special>(</span><span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>);</span>
<span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span>
<span class=identifier>std</span><span class=special>::</span><span class=identifier>pair</span><span class=special>&lt;</span><span class=identifier>iterator</span><span class=special>,</span><span class=keyword>bool</span><span class=special>&gt;</span> <span class=identifier>merge</span><span class=special>(</span>
<span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>,</span><span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>i</span><span class=special>);</span>
<span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span>
<span class=keyword>void</span> <span class=identifier>merge</span><span class=special>(</span>
<span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>,</span>
<span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>first</span><span class=special>,</span>
<span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>last</span><span class=special>);</span>
<span class=comment>// observers:</span> <span class=comment>// observers:</span>
<span class=identifier>key_from_value</span> <span class=identifier>key_extractor</span><span class=special>()</span><span class=keyword>const</span><span class=special>;</span> <span class=identifier>key_from_value</span> <span class=identifier>key_extractor</span><span class=special>()</span><span class=keyword>const</span><span class=special>;</span>
@ -465,6 +476,15 @@ must be a model of <a href="key_extraction.html#key_extractors">
on elements of <code>KeyFromValue::result_type</code>. on elements of <code>KeyFromValue::result_type</code>.
</p> </p>
<h4><a name="types">Nested types</a></h4>
<code>iterator<br>
const_iterator</code>
<blockquote>
These types depend only on <code>node_type</code>.
</blockquote>
<h4><a name="constructors">Constructors, copy and assignment</a></h4> <h4><a name="constructors">Constructors, copy and assignment</a></h4>
<p> <p>
@ -911,6 +931,89 @@ with <code>mod'</code> and <code>back</code> defined in such a way that
<code>key</code> is the internal <code>KeyFromValue</code> object of the index. <code>key</code> is the internal <code>KeyFromValue</code> object of the index.
</blockquote> </blockquote>
<a name="merge"><code>template&lt;typename Index&gt; void merge(Index&amp;&amp; x);</code></a>
<blockquote>
<b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<a href="multi_index_container.html#node_type">node-compatible</a>
<code>multi_index_container</code>. <code>get_allocator()==x.get_allocator()</code>.<br>
<b>Effects:</b>
<blockquote><pre>
<span class=identifier>merge</span><span class=special>(</span><span class=identifier>x</span><span class=special>,</span><span class=identifier>x</span><span class=special>.</span><span class=identifier>begin</span><span class=special>(),</span><span class=identifier>x</span><span class=special>.</span><span class=identifier>end</span><span class=special>());</span>
</pre></blockquote>
</blockquote>
<code>
template&lt;typename Index&gt; std::pair&lt;iterator,bool&gt; merge(<br>
&nbsp;&nbsp;Index&amp;&amp; x,typename std::remove_reference_t&lt;Index&gt;::const_iterator i);
</code>
<blockquote>
<b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<a href="multi_index_container.html#node_type">node-compatible</a>
<code>multi_index_container</code>. <code>get_allocator()==x.get_allocator()</code>.
<code>i</code> is a valid dereferenceable iterator of <code>x</code>.<br>
<b>Effects:</b> Does nothing if the source and destination containers are the same;
otherwise, transfers the node of the element referred to by <code>i</code> into the
<code>multi_index_container</code> to which the destination index belongs if
<ul>
<li>the index is non-unique OR no other element exists with
equivalent key,</li>
<li>AND insertion is allowed by all other indices of the
<code>multi_index_container</code>.</li>
</ul>
Note that no element is copied or destroyed in the process.<br>
<b>Postconditions:</b> If transfer succeeds, for any index in the source container
having the same <code>iterator</code>/<code>const_iterator</code> types as the corresponding
index in the destination container, iterators referring to <code>*i</code>
remain valid and behave as iterators of the destination index.<br>
<b>Returns:</b> The return value is a pair <code>p</code>. <code>p.second</code>
is <code>true</code> if and only if transfer took place or the source and destination
containers are the same. If <code>p.second</code> is <code>true</code>,
<code>p.first</code> points to <code>*i</code>; otherwise, <code>p.first</code>
points to an element that caused the insertion to be banned. Note that more than
one element can be causing insertion not to be allowed.<br>
<b>Complexity:</b> If the source and destination containers are the same,
constant; otherwise, <code>O(I(n)+D(x.size()))</code>.<br>
<b>Exception safety:</b> If the source and destination containers are the same,
<code>nothrow</code>; otherwise strong.
</blockquote>
<code>
template&lt;typename Index&gt; void merge(<br>
&nbsp;&nbsp;Index&amp;&amp; x,<br>
&nbsp;&nbsp;typename std::remove_reference_t&lt;Index&gt;::const_iterator first,<br>
&nbsp;&nbsp;typename std::remove_reference_t&lt;Index&gt;::const_iterator last);
</code>
<blockquote>
<b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<a href="multi_index_container.html#node_type">node-compatible</a>
<code>multi_index_container</code>. <code>get_allocator()==x.get_allocator()</code>.
[<code>first</code>,<code>last</code>) is a valid range of <code>x</code>.<br>
<b>Effects:</b> Does nothing if the source and destination containers are the same;
otherwise, for each node in [<code>first</code>,<code>last</code>), in this order,
the node is transferred to the <code>multi_index_container</code> to which the
destination index belongs if
<ul>
<li>the index is non-unique OR no other element exists with
equivalent key,</li>
<li>AND insertion is allowed by all other indices of the
<code>multi_index_container</code>.</li>
</ul>
Note that no element is copied or destroyed in the process.<br>
<b>Postconditions:</b> For any index in the source container having the same
<code>iterator</code>/<code>const_iterator</code> types as the corresponding
index in the destination container, iterators referring to the transferred elements
remain valid and behave as iterators of the destination index.<br>
<b>Complexity:</b> If the source and destination containers are the same,
constant; otherwise, <code>O(m*(I(n+m)+D(x.size())))</code>, where
<code>m</code> is the number of elements in [<code>first</code>,
<code>last</code>).<br>
<b>Exception safety:</b> If the source and destination containers are the same,
<code>nothrow</code>; otherwise basic.
</blockquote>
<h4><a name="observers">Observers</a></h4> <h4><a name="observers">Observers</a></h4>
<p>Apart from standard <code>key_comp</code> and <code>value_comp</code>, <p>Apart from standard <code>key_comp</code> and <code>value_comp</code>,
@ -1237,7 +1340,7 @@ Ranked indices
<br> <br>
<p>Revised July 29th 2021</p> <p>Revised August 16th 2021</p>
<p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz. <p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz.
Distributed under the Boost Software Distributed under the Boost Software

View File

@ -40,6 +40,7 @@ Key extraction
<ul> <ul>
<li><a href="#complexity_signature">Complexity signature</a></li> <li><a href="#complexity_signature">Complexity signature</a></li>
<li><a href="#instantiation_types">Instantiation types</a></li> <li><a href="#instantiation_types">Instantiation types</a></li>
<li><a href="#types">Nested types</a></li>
<li><a href="#constructors">Constructors, copy and assignment</a></li> <li><a href="#constructors">Constructors, copy and assignment</a></li>
<li><a href="#iterators">Iterators</a></li> <li><a href="#iterators">Iterators</a></li>
<li><a href="#capacity">Capacity operations</a></li> <li><a href="#capacity">Capacity operations</a></li>
@ -316,10 +317,16 @@ plus the requirements for <code>std::list</code> specific list operations at
<span class=comment>// list operations:</span> <span class=comment>// list operations:</span>
<span class=keyword>void</span> <span class=identifier>splice</span><span class=special>(</span><span class=identifier>iterator</span> <span class=identifier>position</span><span class=special>,</span><b>index class name</b><span class=special>&amp;</span> <span class=identifier>x</span><span class=special>);</span> <span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span> <span class=keyword>void</span> <span class=identifier>splice</span><span class=special>(</span><span class=identifier>const_iterator</span> <span class=identifier>position</span><span class=special>,</span><span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>);</span>
<span class=keyword>void</span> <span class=identifier>splice</span><span class=special>(</span><span class=identifier>iterator</span> <span class=identifier>position</span><span class=special>,</span><b>index class name</b><span class=special>&amp;</span> <span class=identifier>x</span><span class=special>,</span><span class=identifier>iterator</span> <span class=identifier>i</span><span class=special>);</span> <span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span>
<span class=identifier>std</span><span class=special>::</span><span class=identifier>pair</span><span class=special>&lt;</span><span class=identifier>iterator</span><span class=special>,</span><span class=keyword>bool</span><span class=special>&gt;</span> <span class=identifier>splice</span><span class=special>(</span>
<span class=identifier>const_iterator</span> <span class=identifier>position</span><span class=special>,</span><span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>,</span>
<span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>i</span><span class=special>);</span>
<span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span>
<span class=keyword>void</span> <span class=identifier>splice</span><span class=special>(</span> <span class=keyword>void</span> <span class=identifier>splice</span><span class=special>(</span>
<span class=identifier>iterator</span> <span class=identifier>position</span><span class=special>,</span><b>index class name</b><span class=special>&amp;</span> <span class=identifier>x</span><span class=special>,</span><span class=identifier>iterator</span> <span class=identifier>first</span><span class=special>,</span><span class=identifier>iterator</span> <span class=identifier>last</span><span class=special>);</span> <span class=identifier>const_iterator</span> <span class=identifier>position</span><span class=special>,</span><span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>,</span>
<span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>first</span><span class=special>,</span>
<span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>last</span><span class=special>);</span>
<span class=keyword>void</span> <span class=identifier>remove</span><span class=special>(</span><span class=keyword>const</span> <span class=identifier>value_type</span><span class=special>&amp;</span> <span class=identifier>value</span><span class=special>);</span> <span class=keyword>void</span> <span class=identifier>remove</span><span class=special>(</span><span class=keyword>const</span> <span class=identifier>value_type</span><span class=special>&amp;</span> <span class=identifier>value</span><span class=special>);</span>
<span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Predicate</span><span class=special>&gt;</span> <span class=keyword>void</span> <span class=identifier>remove_if</span><span class=special>(</span><span class=identifier>Predicate</span> <span class=identifier>pred</span><span class=special>);</span> <span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Predicate</span><span class=special>&gt;</span> <span class=keyword>void</span> <span class=identifier>remove_if</span><span class=special>(</span><span class=identifier>Predicate</span> <span class=identifier>pred</span><span class=special>);</span>
@ -450,6 +457,15 @@ index specifier. Instantiations are dependent on the following types:
<a href="indices.html#tag"><code>tag</code></a>. <a href="indices.html#tag"><code>tag</code></a>.
</p> </p>
<h4><a name="types">Nested types</a></h4>
<code>iterator<br>
const_iterator</code>
<blockquote>
These types depend only on <code>node_type</code>.
</blockquote>
<h4><a name="constructors">Constructors, copy and assignment</a></h4> <h4><a name="constructors">Constructors, copy and assignment</a></h4>
<p> <p>
@ -875,56 +891,116 @@ The syntax and behavior of these operations exactly matches those
of sequenced indices, but the associated complexity bounds differ in general. of sequenced indices, but the associated complexity bounds differ in general.
</p> </p>
<code>void splice(iterator position,<b>index class name</b>&amp; x);</code> <code>template&lt;typename Index&gt; void splice(const_iterator position,Index&amp;&amp; x);</code>
<blockquote> <blockquote>
<b>Requires:</b> <code>position</code> is a valid iterator of the index. <b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<code>&amp;x!=this</code>.<br> <a href="multi_index_container.html#node_type">node-compatible</a>
<b>Effects:</b> Inserts the contents of <code>x</code> before <code>position</code>, <code>multi_index_container</code>.
in the same order as they were in <code>x</code>. Those elements successfully <code>position</code> is a valid iterator of the index, and must be exactly <code>end()</code>
inserted are erased from <code>x</code>.<br> if the source and destination containers are the same.<br>
<b>Complexity:</b> <code>O(shl(end()-position,x.size()) + x.size()*I(n+x.size()) + x.size()*D(x.size()))</code>.<br> <b>Effects:</b>
<b>Exception safety:</b> Basic.<br> <blockquote><pre>
<span class=identifier>splice</span><span class=special>(</span><span class=identifier>position</span><span class=special>,</span><span class=identifier>x</span><span class=special>,</span><span class=identifier>x</span><span class=special>.</span><span class=identifier>begin</span><span class=special>(),</span><span class=identifier>x</span><span class=special>.</span><span class=identifier>end</span><span class=special>());</span>
</pre></blockquote>
</blockquote> </blockquote>
<code>void splice(iterator position,<b>index class name</b>&amp; x,iterator i);</code> <code>
template&lt;typename Index&gt; std::pair&lt;iterator,bool&gt; splice(<br>
&nbsp;&nbsp;const_iterator position,Index&amp;&amp; x,<br>
&nbsp;&nbsp;typename std::remove_reference_t&lt;Index&gt;::const_iterator i);
</code>
<blockquote> <blockquote>
<b>Requires:</b> <code>position</code> is a valid iterator of the index. <b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<code>i</code> is a valid dereferenceable iterator <code>x</code>.<br> <a href="multi_index_container.html#node_type">node-compatible</a>
<b>Effects:</b> Inserts the element pointed to by <code>i</code> before <code>multi_index_container</code>.
<code>position</code>: if insertion is successful, the element is erased from If <code>get_allocator()!=x.get_allocator()</code>,
<code>x</code>. In the special case <code>&amp;x==this</code>, no copy or <code>value_type</code> must be <code>CopyInsertable</code> into the destination
deletion is performed, and the operation is always successful. If <code>multi_index_container</code>.
<code>position==i</code>, no operation is performed.<br> <code>position</code> is a valid iterator of the index.
<b>Postconditions:</b> If <code>&amp;x==this</code>, no iterator or reference <code>i</code> is a valid dereferenceable iterator of <code>x</code>.<br>
is invalidated.<br> <b>Effects:</b>
<b>Complexity:</b> If <code>&amp;x==this</code>, <code>O(rel(position,i,i+1))</code>; <ul>
otherwise <code>O(shl(end()-position,1) + I(n) + D(n))</code>.<br> <li><b>(Same container)</b> if the source and destination containers are the same, repositions
<b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>; the element pointed to by <code>i</code> before <code>position</code> unless both
otherwise, strong.<br> iterators refer to the same element.</li>
<li><b>(Transfer splice)</b> else, if <code>get_allocator()==x.get_allocator()</code>,
transfers the node of the element referred to by <code>i</code> into the
destination <code>multi_index_container</code> right before <code>position</code>
if insertion is allowed by all other indices of the <code>multi_index_container</code>.</li>
<li><b>(Destructive splice)</b> else, insertion of <code>*i</code> is tried before
<code>position</code>; if the operation is successful, the element is erased from <code>x</code>.
</li>
</ul>
<b>Postconditions:</b> If transfer succeeds, for any index in the source container
having the same <code>iterator</code>/<code>const_iterator</code> types as the corresponding
index in the destination container, iterators referring to <code>*i</code>
remain valid and behave as iterators of the destination index.<br>
<b>Returns:</b> The return value is a pair <code>p</code>. <code>p.second</code>
is <code>true</code> if and only if insertion (either through transfer or copy insertion)
took place or the source and destination containers are the same.
If <code>p.second</code> is <code>true</code>,
<code>p.first</code> points to the inserted element or to <code>*i</code> if the
source and destination containers are the same; otherwise, <code>p.first</code>
points to an element that caused the insertion to be banned. Note that more than
one element can be causing insertion not to be allowed.<br>
<b>Complexity:</b> If the source and destination containers are the same,
<code>O(rel(position,i',i'+1))</code>, where <code>i'</code> is the projection
of <code>i</code> into the index of <code>position</code>;
otherwise, <code>O(shl(end()-position,1) + I(n) + D(x.size()))</code>.<br>
<b>Exception safety:</b> If the source and destination containers are the same,
<code>nothrow</code>; otherwise strong.<br>
<b>Implementation note:</b> The destructive variant of this operation is provided
for reasons of backwards compatibility with previous versions of this library where
allocator equality was not required.
</blockquote> </blockquote>
<code>void splice(iterator position,<b>index class name&amp;</b> x,iterator first,iterator last);</code> <code>
template&lt;typename Index&gt; void splice(<br>
&nbsp;&nbsp;const_iterator position,Index&amp;&amp; x,<br>
&nbsp;&nbsp;typename std::remove_reference_t&lt;Index&gt;::const_iterator first,<br>
&nbsp;&nbsp;typename std::remove_reference_t&lt;Index&gt;::const_iterator last);
</code>
<blockquote> <blockquote>
<b>Requires:</b> <code>position</code> is a valid iterator of the index. <b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<code>first</code> and <code>last</code> are valid iterators of <code>x</code>. <a href="multi_index_container.html#node_type">node-compatible</a>
<code>last</code> is reachable from <code>first</code>. <code>position</code> <code>multi_index_container</code>.
is not in the range [<code>first</code>,<code>last</code>).<br> If <code>get_allocator()!=x.get_allocator()</code>,
<b>Effects:</b> For each element in the range [<code>first</code>,<code>last</code>), <code>value_type</code> must be <code>CopyInsertable</code> into the destination
insertion is tried before <code>position</code>; if the operation is successful, <code>multi_index_container</code>.
the element is erased from <code>x</code>. In the special case <code>position</code> is a valid iterator of the index and does not point to any element in
<code>&amp;x==this</code>, no copy or deletion is performed, and insertions are [<code>first</code>,<code>last</code>).
always successful.<br> [<code>first</code>,<code>last</code>) is a valid range of <code>x</code>.<br>
<b>Postconditions:</b> If <code>&amp;x==this</code>, no iterator or reference <b>Effects:</b>
is invalidated.<br> <ul>
<b>Complexity:</b> If <code>&amp;x==this</code>, <li><b>(Same container)</b> if the source and destination containers are the same, repositions all the elements
<code>O(rel(position,first,last))</code>; otherwise of [<code>first</code>,<code>last</code>), in this order, before <code>position</code>.
<code>O(shl(end()-position,m) + m*I(n+m) + m*D(x.size()))</code> </li>
where <code>m</code> is the number of elements in [<code>first</code>,<code>last</code>).<br> <li><b>(Transfer splice)</b> else, if <code>get_allocator()==x.get_allocator()</code>, then, for each node in
<b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>; [<code>first</code>,<code>last</code>), in this order, the node is transferred to the
otherwise, basic.<br> <code>multi_index_container</code> right before <code>position</code>
if insertion is allowed by all other indices of the
<code>multi_index_container</code>.</li>
<li><b>(Destructive splice)</b> else, for each element in [<code>first</code>,<code>last</code>), in this order,
insertion is tried before <code>position</code>; if the operation is successful, the
element is erased from <code>x</code>.
</li>
</ul>
<b>Postconditions:</b> For any index in the source container having the same
<code>iterator</code>/<code>const_iterator</code> types as the corresponding
index in the destination container, iterators referring to the transferred elements
remain valid and behave as iterators of the destination index.<br>
<b>Complexity:</b> If <code>&amp;x==this</code>, <code>O(rel(position,first,last))</code>;
else, if the source and destination containers are the same, <code>O(n)</code>;
otherwise, <code>O(shl(end()-position,m) + m*(I(n+m) + D(x.size())))</code>, where
<code>m</code> is the number of elements in [<code>first</code>,<code>last</code>).<br>
<b>Exception safety:</b> If the source and destination containers are the same,
<code>nothrow</code>; otherwise basic.<br>
<b>Implementation note:</b> The destructive variant of this operation is provided
for reasons of backwards compatibility with previous versions of this library where
allocator equality was not required.
</blockquote> </blockquote>
<code>void remove(const value_type&amp; value);</code> <code>void remove(const value_type&amp; value);</code>
@ -973,20 +1049,22 @@ is the number of elements erased.<br>
<code>void merge(index class name&amp; x);</code> <code>void merge(index class name&amp; x);</code>
<blockquote> <blockquote>
<b>Requires:</b> <code>std::less&lt;value_type&gt;</code> induces a <b>Requires:</b>
Either <code>get_allocator()==x.get_allocator()</code> or <code>value_type</code>
is <code>CopyInsertable</code> into the <code>multi_index_container</code>.
<code>std::less&lt;value_type&gt;</code> induces a
strict weak ordering over <code>value_type</code>. strict weak ordering over <code>value_type</code>.
Both the index and <code>x</code> are sorted according to Both the index and <code>x</code> are sorted according to
<code>std::less&lt;value_type&gt;</code>.<br> <code>std::less&lt;value_type&gt;</code>.<br>
<b>Effects:</b> Attempts to insert every element of <code>x</code> into the <b>Effects:</b> Attempts to splice every element of <code>x</code> into the
corresponding position of the index (according to the order). Elements corresponding position of the index (according to the order). The resulting sequence
successfully inserted are erased from <code>x</code>. The resulting sequence
is stable, i.e. equivalent elements of either container preserve their is stable, i.e. equivalent elements of either container preserve their
relative position. In the special case <code>&amp;x==this</code>, no operation relative position. In the special case <code>&amp;x==this</code>, no operation
is performed.<br> is performed.<br>
<b>Postconditions:</b> Elements in the index and remaining elements in <b>Postconditions:</b> Elements in the index and remaining elements in
<code>x</code> are sorted. <code>x</code> are sorted.
Validity of iterators to the index and of non-erased elements of <code>x</code> Validity of iterators and references is preserved, except to elements removed
references is preserved.<br> from <code>x</code> if <code>get_allocator()!=x.get_allocator()</code>.<br>
<b>Complexity:</b> If <code>&amp;x==this</code>, constant; otherwise <b>Complexity:</b> If <code>&amp;x==this</code>, constant; otherwise
<code>O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))</code>.<br> <code>O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))</code>.<br>
<b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>; <b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>;
@ -996,19 +1074,20 @@ otherwise, basic.<br>
<code>template &lt;typename Compare> void merge(index class name&amp; x,Compare comp);</code> <code>template &lt;typename Compare> void merge(index class name&amp; x,Compare comp);</code>
<blockquote> <blockquote>
<b>Requires:</b> <code>Compare</code> induces a <b>Requires:</b>
strict weak ordering over <code>value_type</code>. Either <code>get_allocator()==x.get_allocator()</code> or <code>value_type</code>
is <code>CopyInsertable</code> into the <code>multi_index_container</code>.
<code>Compare</code> induces a strict weak ordering over <code>value_type</code>.
Both the index and <code>x</code> are sorted according to <code>comp</code>.<br> Both the index and <code>x</code> are sorted according to <code>comp</code>.<br>
<b>Effects:</b> Attempts to insert every element of <code>x</code> into the <b>Effects:</b> Attempts to splice every element of <code>x</code> into the
corresponding position of the index (according to <code>comp</code>). corresponding position of the index (according to <code>comp</code>). The resulting
Elements successfully inserted are erased from <code>x</code>. The resulting
sequence is stable, i.e. equivalent elements of either container preserve sequence is stable, i.e. equivalent elements of either container preserve
their relative position. In the special case <code>&amp;x==this</code>, no their relative position. In the special case <code>&amp;x==this</code>, no
operation is performed.<br> operation is performed.<br>
<b>Postconditions:</b> Elements in the index and remaining elements in <b>Postconditions:</b> Elements in the index and remaining elements in
<code>x</code> are sorted according to <code>comp</code>. <code>x</code> are sorted according to <code>comp</code>.
Validity of iterators to the index and of non-erased elements of <code>x</code> Validity of iterators and references is preserved, except to elements removed
references is preserved.<br> from <code>x</code> if <code>get_allocator()!=x.get_allocator()</code>.<br>
<b>Complexity:</b> If <code>&amp;x==this</code>, constant; otherwise <b>Complexity:</b> If <code>&amp;x==this</code>, constant; otherwise
<code>O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))</code>.<br> <code>O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))</code>.<br>
<b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>; <b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>;
@ -1160,9 +1239,9 @@ Key extraction
<br> <br>
<p>Revised May 9th 2020</p> <p>Revised August 16th 2021</p>
<p>&copy; Copyright 2003-2020 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz. <p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz.
Distributed under the Boost Software Distributed under the Boost Software
License, Version 1.0. (See accompanying file <a href="../../../../LICENSE_1_0.txt"> License, Version 1.0. (See accompanying file <a href="../../../../LICENSE_1_0.txt">
LICENSE_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt"> LICENSE_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt">

View File

@ -42,6 +42,7 @@ Hashed indices
<ul> <ul>
<li><a href="#complexity_signature">Complexity signature</a></li> <li><a href="#complexity_signature">Complexity signature</a></li>
<li><a href="#instantiation_types">Instantiation types</a></li> <li><a href="#instantiation_types">Instantiation types</a></li>
<li><a href="#types">Nested types</a></li>
<li><a href="#rank_operations">Rank operations</a></li> <li><a href="#rank_operations">Rank operations</a></li>
<li><a href="#serialization">Serialization</a></li> <li><a href="#serialization">Serialization</a></li>
</ul> </ul>
@ -473,6 +474,15 @@ These types are subject to the same requirements as specified for
<a href="ord_indices.html#instantiation_types">ordered indices</a>. <a href="ord_indices.html#instantiation_types">ordered indices</a>.
</p> </p>
<h4><a name="types">Nested types</a></h4>
<code>iterator<br>
const_iterator</code>
<blockquote>
These types depend only on <code>node_type</code>.
</blockquote>
<h4><a name="rank_operations">Rank operations</a></h4> <h4><a name="rank_operations">Rank operations</a></h4>
<p> <p>
@ -637,7 +647,7 @@ Hashed indices
<br> <br>
<p>Revised July 29th 2021</p> <p>Revised August 14th 2021</p>
<p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz. <p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz.
Distributed under the Boost Software Distributed under the Boost Software

View File

@ -40,6 +40,7 @@ Random access indices
<ul> <ul>
<li><a href="#complexity_signature">Complexity signature</a></li> <li><a href="#complexity_signature">Complexity signature</a></li>
<li><a href="#instantiation_types">Instantiation types</a></li> <li><a href="#instantiation_types">Instantiation types</a></li>
<li><a href="#types">Nested types</a></li>
<li><a href="#constructors">Constructors, copy and assignment</a></li> <li><a href="#constructors">Constructors, copy and assignment</a></li>
<li><a href="#iterators">Iterators</a></li> <li><a href="#iterators">Iterators</a></li>
<li><a href="#capacity">Capacity operations</a></li> <li><a href="#capacity">Capacity operations</a></li>
@ -299,10 +300,16 @@ most important differences are:
<span class=comment>// list operations:</span> <span class=comment>// list operations:</span>
<span class=keyword>void</span> <span class=identifier>splice</span><span class=special>(</span><span class=identifier>iterator</span> <span class=identifier>position</span><span class=special>,</span><b>index class name</b><span class=special>&amp;</span> <span class=identifier>x</span><span class=special>);</span> <span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span> <span class=keyword>void</span> <span class=identifier>splice</span><span class=special>(</span><span class=identifier>const_iterator</span> <span class=identifier>position</span><span class=special>,</span><span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>);</span>
<span class=keyword>void</span> <span class=identifier>splice</span><span class=special>(</span><span class=identifier>iterator</span> <span class=identifier>position</span><span class=special>,</span><b>index class name</b><span class=special>&amp;</span> <span class=identifier>x</span><span class=special>,</span><span class=identifier>iterator</span> <span class=identifier>i</span><span class=special>);</span> <span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span>
<span class=identifier>std</span><span class=special>::</span><span class=identifier>pair</span><span class=special>&lt;</span><span class=identifier>iterator</span><span class=special>,</span><span class=keyword>bool</span><span class=special>&gt;</span> <span class=identifier>splice</span><span class=special>(</span>
<span class=identifier>const_iterator</span> <span class=identifier>position</span><span class=special>,</span><span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>,</span>
<span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>i</span><span class=special>);</span>
<span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Index</span><span class=special>&gt;</span>
<span class=keyword>void</span> <span class=identifier>splice</span><span class=special>(</span> <span class=keyword>void</span> <span class=identifier>splice</span><span class=special>(</span>
<span class=identifier>iterator</span> <span class=identifier>position</span><span class=special>,</span><b>index class name</b><span class=special>&amp;</span> <span class=identifier>x</span><span class=special>,</span><span class=identifier>iterator</span> <span class=identifier>first</span><span class=special>,</span><span class=identifier>iterator</span> <span class=identifier>last</span><span class=special>);</span> <span class=identifier>const_iterator</span> <span class=identifier>position</span><span class=special>,</span><span class=identifier>Index</span><span class=special>&amp;&amp;</span> <span class=identifier>x</span><span class=special>,</span>
<span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>first</span><span class=special>,</span>
<span class=keyword>typename</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>remove_reference_t</span><span class=special>&lt;</span><span class=identifier>Index</span><span class=special>&gt;::</span><span class=identifier>const_iterator</span> <span class=identifier>last</span><span class=special>);</span>
<span class=keyword>void</span> <span class=identifier>remove</span><span class=special>(</span><span class=keyword>const</span> <span class=identifier>value_type</span><span class=special>&amp;</span> <span class=identifier>value</span><span class=special>);</span> <span class=keyword>void</span> <span class=identifier>remove</span><span class=special>(</span><span class=keyword>const</span> <span class=identifier>value_type</span><span class=special>&amp;</span> <span class=identifier>value</span><span class=special>);</span>
<span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Predicate</span><span class=special>&gt;</span> <span class=keyword>void</span> <span class=identifier>remove_if</span><span class=special>(</span><span class=identifier>Predicate</span> <span class=identifier>pred</span><span class=special>);</span> <span class=keyword>template</span><span class=special>&lt;</span><span class=keyword>typename</span> <span class=identifier>Predicate</span><span class=special>&gt;</span> <span class=keyword>void</span> <span class=identifier>remove_if</span><span class=special>(</span><span class=identifier>Predicate</span> <span class=identifier>pred</span><span class=special>);</span>
@ -420,6 +427,15 @@ index specifier. Instantiations are dependent on the following types:
<a href="indices.html#tag"><code>tag</code></a>. <a href="indices.html#tag"><code>tag</code></a>.
</p> </p>
<h4><a name="types">Nested types</a></h4>
<code>iterator<br>
const_iterator</code>
<blockquote>
These types depend only on <code>node_type</code>.
</blockquote>
<h4><a name="constructors">Constructors, copy and assignment</a></h4> <h4><a name="constructors">Constructors, copy and assignment</a></h4>
<p> <p>
@ -802,62 +818,122 @@ is rethrown.
<p> <p>
Sequenced indices provide the full set of list operations found in Sequenced indices provide the full set of list operations found in
<code>std::list</code>; the semantics of these member functions, however, <code>std::list</code>, extended in some cases such as <code>splice</code>;
differ from that of <code>std::list</code> in some cases as insertions the semantics of these member functions, however,
might not succeed due to banning by other indices. Similarly, the complexity typically differs from that of <code>std::list</code> as insertions
may not succeed due to banning by other indices. Similarly, the complexity
of the operations may depend on the other indices belonging to the of the operations may depend on the other indices belonging to the
same <code>multi_index_container</code>. same <code>multi_index_container</code>.
</p> </p>
<code>void splice(iterator position,<b>index class name</b>&amp; x);</code> <a name="splice"><code>template&lt;typename Index&gt; void splice(const_iterator position,Index&amp;&amp; x);</code></a>
<blockquote> <blockquote>
<b>Requires:</b> <code>position</code> is a valid iterator of the index. <b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<code>&amp;x!=this</code>.<br> <a href="multi_index_container.html#node_type">node-compatible</a>
<b>Effects:</b> Inserts the contents of <code>x</code> before <code>position</code>, <code>multi_index_container</code>.
in the same order as they were in <code>x</code>. Those elements successfully <code>position</code> is a valid iterator of the index, and must be exactly <code>end()</code>
inserted are erased from <code>x</code>.<br> if the source and destination containers are the same.<br>
<b>Complexity:</b> <code>O(x.size()*I(n+x.size()) + x.size()*D(x.size()))</code>.<br> <b>Effects:</b>
<b>Exception safety:</b> Basic.<br> <blockquote><pre>
<span class=identifier>splice</span><span class=special>(</span><span class=identifier>position</span><span class=special>,</span><span class=identifier>x</span><span class=special>,</span><span class=identifier>x</span><span class=special>.</span><span class=identifier>begin</span><span class=special>(),</span><span class=identifier>x</span><span class=special>.</span><span class=identifier>end</span><span class=special>());</span>
</pre></blockquote>
</blockquote> </blockquote>
<code>void splice(iterator position,<b>index class name</b>&amp; x,iterator i);</code> <code>
template&lt;typename Index&gt; std::pair&lt;iterator,bool&gt; splice(<br>
&nbsp;&nbsp;const_iterator position,Index&amp;&amp; x,<br>
&nbsp;&nbsp;typename std::remove_reference_t&lt;Index&gt;::const_iterator i);
</code>
<blockquote> <blockquote>
<b>Requires:</b> <code>position</code> is a valid iterator of the index. <b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<code>i</code> is a valid dereferenceable iterator <code>x</code>.<br> <a href="multi_index_container.html#node_type">node-compatible</a>
<b>Effects:</b> Inserts the element pointed to by <code>i</code> before <code>multi_index_container</code>.
<code>position</code>: if insertion is successful, the element is erased from If <code>get_allocator()!=x.get_allocator()</code>,
<code>x</code>. In the special case <code>&amp;x==this</code>, no copy or <code>value_type</code> must be <code>CopyInsertable</code> into the destination
deletion is performed, and the operation is always successful. If <code>multi_index_container</code>.
<code>position==i</code>, no operation is performed.<br> <code>position</code> is a valid iterator of the index.
<b>Postconditions:</b> If <code>&amp;x==this</code>, no iterator or reference <code>i</code> is a valid dereferenceable iterator of <code>x</code>.<br>
is invalidated.<br> <b>Effects:</b>
<b>Complexity:</b> If <code>&amp;x==this</code>, constant; otherwise <ul>
<code>O(I(n) + D(n))</code>.<br> <li><b>(Same container)</b> if the source and destination containers are the same, repositions
<b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>; the element pointed to by <code>i</code> before <code>position</code> unless both
otherwise, strong.<br> iterators refer to the same element.</li>
<li><b>(Transfer splice)</b> else, if <code>get_allocator()==x.get_allocator()</code>,
transfers the node of the element referred to by <code>i</code> into the
destination <code>multi_index_container</code> right before <code>position</code>
if insertion is allowed by all other indices of the <code>multi_index_container</code>.</li>
<li><b>(Destructive splice)</b> else, insertion of <code>*i</code> is tried before
<code>position</code>; if the operation is successful, the element is erased from <code>x</code>.
</li>
</ul>
<b>Postconditions:</b> If transfer succeeds, for any index in the source container
having the same <code>iterator</code>/<code>const_iterator</code> types as the corresponding
index in the destination container, iterators referring to <code>*i</code>
remain valid and behave as iterators of the destination index.<br>
<b>Returns:</b> The return value is a pair <code>p</code>. <code>p.second</code>
is <code>true</code> if and only if insertion (either through transfer or copy insertion)
took place or the source and destination containers are the same.
If <code>p.second</code> is <code>true</code>,
<code>p.first</code> points to the inserted element or to <code>*i</code> if the
source and destination containers are the same; otherwise, <code>p.first</code>
points to an element that caused the insertion to be banned. Note that more than
one element can be causing insertion not to be allowed.<br>
<b>Complexity:</b> If the source and destination containers are the same,
constant; otherwise, <code>O(I(n)+D(x.size()))</code>.<br>
<b>Exception safety:</b> If the source and destination containers are the same,
<code>nothrow</code>; otherwise strong.<br>
<b>Implementation note:</b> The destructive variant of this operation is provided
for reasons of backwards compatibility with previous versions of this library where
allocator equality was not required.
</blockquote> </blockquote>
<code>void splice(iterator position,<b>index class name&amp;</b> x,iterator first,iterator last);</code> <code>
template&lt;typename Index&gt; void splice(<br>
&nbsp;&nbsp;const_iterator position,Index&amp;&amp; x,<br>
&nbsp;&nbsp;typename std::remove_reference_t&lt;Index&gt;::const_iterator first,<br>
&nbsp;&nbsp;typename std::remove_reference_t&lt;Index&gt;::const_iterator last);
</code>
<blockquote> <blockquote>
<b>Requires:</b> <code>position</code> is a valid iterator of the index. <b>Requires:</b> <code>x</code> is a non-const reference to an index of a
<code>first</code> and <code>last</code> are valid iterators of <code>x</code>. <a href="multi_index_container.html#node_type">node-compatible</a>
<code>last</code> is reachable from <code>first</code>. <code>position</code> <code>multi_index_container</code>.
is not in the range [<code>first</code>,<code>last</code>).<br> If <code>get_allocator()!=x.get_allocator()</code>,
<b>Effects:</b> For each element in the range [<code>first</code>,<code>last</code>), <code>value_type</code> must be <code>CopyInsertable</code> into the destination
insertion is tried before <code>position</code>; if the operation is successful, <code>multi_index_container</code>.
the element is erased from <code>x</code>. In the special case <code>position</code> is a valid iterator of the index and does not point to any element in
<code>&amp;x==this</code>, no copy or deletion is performed, and insertions are [<code>first</code>,<code>last</code>).
always successful.<br> [<code>first</code>,<code>last</code>) is a valid range of <code>x</code>.<br>
<b>Postconditions:</b> If <code>&amp;x==this</code>, no iterator or reference <b>Effects:</b>
is invalidated.<br> <ul>
<b>Complexity:</b> If <code>&amp;x==this</code>, constant; otherwise <li><b>(Same container)</b> if the source and destination containers are the same, repositions all the elements
<code>O(m*I(n+m) + m*D(x.size()))</code> where <code>m</code> is the number of [<code>first</code>,<code>last</code>), in this order, before <code>position</code>.
of elements in [<code>first</code>,<code>last</code>).<br> </li>
<b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>; <li><b>(Transfer splice)</b> else, if <code>get_allocator()==x.get_allocator()</code>, then, for each node in
otherwise, basic.<br> [<code>first</code>,<code>last</code>), in this order, the node is transferred to the
<code>multi_index_container</code> right before <code>position</code>
if insertion is allowed by all other indices of the
<code>multi_index_container</code>.</li>
<li><b>(Destructive splice)</b> else, for each element in [<code>first</code>,<code>last</code>), in this order,
insertion is tried before <code>position</code>; if the operation is successful, the
element is erased from <code>x</code>.
</li>
</ul>
<b>Postconditions:</b> For any index in the source container having the same
<code>iterator</code>/<code>const_iterator</code> types as the corresponding
index in the destination container, iterators referring to the transferred elements
remain valid and behave as iterators of the destination index.<br>
<b>Complexity:</b> If <code>&amp;x==this</code>, constant; else, if
the source and destination containers are the same, <code>O(m)</code>;
otherwise, <code>O(m*(I(n+m)+D(x.size())))</code>, where
<code>m</code> is the number of elements in [<code>first</code>,<code>last</code>).<br>
<b>Exception safety:</b> If the source and destination containers are the same,
<code>nothrow</code>; otherwise basic.<br>
<b>Implementation note:</b> The destructive variant of this operation is provided
for reasons of backwards compatibility with previous versions of this library where
allocator equality was not required.
</blockquote> </blockquote>
<code>void remove(const value_type&amp; value);</code> <code>void remove(const value_type&amp; value);</code>
@ -906,20 +982,22 @@ is the number of elements erased.<br>
<code>void merge(index class name&amp; x);</code> <code>void merge(index class name&amp; x);</code>
<blockquote> <blockquote>
<b>Requires:</b> <code>std::less&lt;value_type&gt;</code> induces a <b>Requires:</b>
Either <code>get_allocator()==x.get_allocator()</code> or <code>value_type</code>
is <code>CopyInsertable</code> into the <code>multi_index_container</code>.
<code>std::less&lt;value_type&gt;</code> induces a
strict weak ordering over <code>value_type</code>. strict weak ordering over <code>value_type</code>.
Both the index and <code>x</code> are sorted according to Both the index and <code>x</code> are sorted according to
<code>std::less&lt;value_type&gt;</code>.<br> <code>std::less&lt;value_type&gt;</code>.<br>
<b>Effects:</b> Attempts to insert every element of <code>x</code> into the <b>Effects:</b> Attempts to splice every element of <code>x</code> into the
corresponding position of the index (according to the order). Elements corresponding position of the index (according to the order). The resulting sequence
successfully inserted are erased from <code>x</code>. The resulting sequence
is stable, i.e. equivalent elements of either container preserve their is stable, i.e. equivalent elements of either container preserve their
relative position. In the special case <code>&amp;x==this</code>, no operation relative position. In the special case <code>&amp;x==this</code>, no operation
is performed.<br> is performed.<br>
<b>Postconditions:</b> Elements in the index and remaining elements in <b>Postconditions:</b> Elements in the index and remaining elements in
<code>x</code> are sorted. <code>x</code> are sorted.
Validity of iterators to the index and of non-erased elements of <code>x</code> Validity of iterators and references is preserved, except to elements removed
references is preserved.<br> from <code>x</code> if <code>get_allocator()!=x.get_allocator()</code>.<br>
<b>Complexity:</b> If <code>&amp;x==this</code>, constant; otherwise <b>Complexity:</b> If <code>&amp;x==this</code>, constant; otherwise
<code>O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))</code>.<br> <code>O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))</code>.<br>
<b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>; <b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>;
@ -929,19 +1007,20 @@ otherwise, basic.<br>
<code>template &lt;typename Compare&gt; void merge(index class name&amp; x,Compare comp);</code> <code>template &lt;typename Compare&gt; void merge(index class name&amp; x,Compare comp);</code>
<blockquote> <blockquote>
<b>Requires:</b> <code>Compare</code> induces a <b>Requires:</b>
strict weak ordering over <code>value_type</code>. Either <code>get_allocator()==x.get_allocator()</code> or <code>value_type</code>
is <code>CopyInsertable</code> into the <code>multi_index_container</code>.
<code>Compare</code> induces a strict weak ordering over <code>value_type</code>.
Both the index and <code>x</code> are sorted according to <code>comp</code>.<br> Both the index and <code>x</code> are sorted according to <code>comp</code>.<br>
<b>Effects:</b> Attempts to insert every element of <code>x</code> into the <b>Effects:</b> Attempts to splice every element of <code>x</code> into the
corresponding position of the index (according to <code>comp</code>). corresponding position of the index (according to <code>comp</code>). The resulting
Elements successfully inserted are erased from <code>x</code>. The resulting
sequence is stable, i.e. equivalent elements of either container preserve sequence is stable, i.e. equivalent elements of either container preserve
their relative position. In the special case <code>&amp;x==this</code>, no their relative position. In the special case <code>&amp;x==this</code>, no
operation is performed.<br> operation is performed.<br>
<b>Postconditions:</b> Elements in the index and remaining elements in <b>Postconditions:</b> Elements in the index and remaining elements in
<code>x</code> are sorted according to <code>comp</code>. <code>x</code> are sorted according to <code>comp</code>.
Validity of iterators to the index and of non-erased elements of <code>x</code> Validity of iterators and references is preserved, except to elements removed
references is preserved.<br> from <code>x</code> if <code>get_allocator()!=x.get_allocator()</code>.<br>
<b>Complexity:</b> If <code>&amp;x==this</code>, constant; otherwise <b>Complexity:</b> If <code>&amp;x==this</code>, constant; otherwise
<code>O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))</code>.<br> <code>O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))</code>.<br>
<b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>; <b>Exception safety:</b> If <code>&amp;x==this</code>, <code>nothrow</code>;
@ -1095,9 +1174,9 @@ Random access indices
<br> <br>
<p>Revised May 9th 2020</p> <p>Revised August 16th 2021</p>
<p>&copy; Copyright 2003-2020 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz. <p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz.
Distributed under the Boost Software Distributed under the Boost Software
License, Version 1.0. (See accompanying file <a href="../../../../LICENSE_1_0.txt"> License, Version 1.0. (See accompanying file <a href="../../../../LICENSE_1_0.txt">
LICENSE_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt"> LICENSE_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt">

View File

@ -72,6 +72,34 @@ Acknowledgements
<li>Added <code>contains</code> to key-based indices <li>Added <code>contains</code> to key-based indices
(<a href="https://github.com/boostorg/multi_index/issues/35">issue #35</a>). (<a href="https://github.com/boostorg/multi_index/issues/35">issue #35</a>).
</li> </li>
<li>Added <code>merge</code> operations to key-based indices. The functionality goes beyond
the standard specification for (unordered) associative containers in a number of ways,
most notably:
<ul>
<li>The source index can be of any type, including non key-based indices.</li>
<li>Partial merge is provided: for instance, <code>x.merge(y,first,last)</code>
merges only the elements of <code>y</code> within [<code>first</code>,<code>last</code>).
</li>
</ul>
</li>
<li>Previous versions of <code>splice</code> for sequenced and random access indices
were destructive, i.e. elements were copy-inserted into the destination and then erased
from the source. Now, <code>splice</code> is based on node transfer much as <code>merge</code>
in key-based indices, and has been similarly extended to accept source indices of any type:
in fact, <code>splice</code> can be regarded as a frontend to the same functionality
provided by <code>merge</code> in key-based indices. For reasons of backwards compatibility,
the destructive behavior of <code>splice</code> has been retained in the case that the
source and destination containers have unequal allocators.
</li>
<li>The fact has been documented that index iterator types do only depend on <code>node_type</code>,
(except for hashed indices, where uniqueness/non-uniqueness is also a dependency). This has
implications on the validity of iterators to elements transferred by <code>merge</code> or
<code>splice</code>. This property is a variant of what has been called
<a href="https://wg21.link/n2980">SCARY iterators</a> in the C++ standard mailing lists.
SCARYness is currently (August 2021) not mandated for standard containers.
</li>
<li>Iterator SCARYness is now also preserved in <a href="tutorial/debug.html#safe_mode">safe mode</a>.
</li>
</ul> </ul>
</p> </p>
@ -88,7 +116,7 @@ Acknowledgements
<p> <p>
<ul> <ul>
<li> <li>
Added <a href="tutorial/basics.html#node_handling">node extraction and insertion</a> Added <a href="tutorial/indices.html#node_handling">node extraction and insertion</a>
following the analogous interface of associative containers as introduced in C++17. following the analogous interface of associative containers as introduced in C++17.
This feature has also been extended to non key-based indices, in contrast to C++ This feature has also been extended to non key-based indices, in contrast to C++
standard library sequence containers, which do not provide such functionality. standard library sequence containers, which do not provide such functionality.
@ -695,7 +723,7 @@ Acknowledgements
<br> <br>
<p>Revised July 29th 2021</p> <p>Revised August 16th 2021</p>
<p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz. <p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz.
Distributed under the Boost Software Distributed under the Boost Software

View File

@ -62,7 +62,6 @@ Index types
</ul> </ul>
</li> </li>
<li><a href="#projection">Projection of iterators</a></li> <li><a href="#projection">Projection of iterators</a></li>
<li><a href="#node_handling">Node handling operations</a></li>
<li><a href="#complexity">Complexity and exception safety</a></li> <li><a href="#complexity">Complexity and exception safety</a></li>
</ul> </ul>
@ -1217,72 +1216,6 @@ When provided, <code>project</code> can also be used with
<a href="#tagging">tags</a>. <a href="#tagging">tags</a>.
</p> </p>
<h2><a name="node_handling">Node handling operations</a></h2>
<p>
Using direct node manipulation, elements can be passed between
<code>multi_index_container</code>s without actually copying them:
</p>
<blockquote><pre>
<span class=comment>// move an employee to the retiree archive</span>
<span class=keyword>void</span> <span class=identifier>move_to_retirement</span><span class=special>(</span><span class=keyword>int</span> <span class=identifier>ssnumber</span><span class=special>,</span><span class=identifier>employee_set</span><span class=special>&amp;</span> <span class=identifier>es</span><span class=special>,</span><span class=identifier>employee_set</span><span class=special>&amp;</span> <span class=identifier>archive</span><span class=special>)</span>
<span class=special>{</span>
<span class=comment>// extract the employee with given SS number to a node handle</span>
<span class=identifier>employee_set_by_ssn</span><span class=special>::</span><span class=identifier>node_type</span> <span class=identifier>node</span><span class=special>=</span><span class=identifier>es</span><span class=special>.</span><span class=identifier>get</span><span class=special>&lt;</span><span class=identifier>ssn</span><span class=special>&gt;().</span><span class=identifier>extract</span><span class=special>(</span><span class=identifier>ssnumber</span><span class=special>);</span>
<span class=keyword>if</span><span class=special>(!</span><span class=identifier>node</span><span class=special>.</span><span class=identifier>empty</span><span class=special>()){</span> <span class=comment>// employee found
// re-insert into archive (note the use of std::move)</span>
<span class=identifier>archive</span><span class=special>.</span><span class=identifier>insert</span><span class=special>(</span><span class=identifier>std</span><span class=special>::</span><span class=identifier>move</span><span class=special>(</span><span class=identifier>node</span><span class=special>));</span>
<span class=special>}</span>
<span class=special>}</span>
</pre></blockquote>
<p>
In the example, the internal node is transferred as-is from <code>es</code> to <code>archive</code>,
which is more efficient than erasing from the source and recreating in destination.
<code>node_type</code> is a move-only class used to pass nodes around, and its interface follows
that of the <a href="https://en.cppreference.com/w/cpp/container/node_handle">homonym type</a>
for C++ associative containers (set containers version). Boost.MultiIndex provides node extraction
and insertion operations for all index types, including sequenced ones (by contrast,
<code>std::list</code> does not have such features):
</p>
<blockquote><pre>
<span class=identifier>multi_index_container</span><span class=special>&lt;</span>
<span class=keyword>int</span><span class=special>,</span>
<span class=identifier>indexed_by</span><span class=special>&lt;</span>
<span class=identifier>sequenced</span><span class=special>&lt;&gt;,</span>
<span class=identifier>ordered_unique</span><span class=special>&lt;</span><span class=identifier>identity</span><span class=special>&lt;</span><span class=keyword>int</span><span class=special>&gt;</span> <span class=special>&gt;</span>
<span class=special>&gt;</span>
<span class=special>&gt;</span> <span class=identifier>src</span><span class=special>;</span>
<span class=identifier>multi_index_container</span><span class=special>&lt;</span>
<span class=keyword>int</span><span class=special>,</span>
<span class=identifier>indexed_by</span><span class=special>&lt;</span>
<span class=identifier>sequenced</span><span class=special>&lt;&gt;,</span>
<span class=identifier>ordered_non_unique</span><span class=special>&lt;</span><span class=identifier>identity</span><span class=special>&lt;</span><span class=keyword>int</span><span class=special>&gt;,</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>greater</span><span class=special>&lt;</span><span class=keyword>int</span><span class=special>&gt;</span> <span class=special>&gt;</span>
<span class=special>&gt;</span>
<span class=special>&gt;</span> <span class=identifier>dst</span><span class=special>;</span>
<span class=special>...</span>
<span class=comment>// transfer even numbers from src to dst</span>
<span class=keyword>for</span><span class=special>(</span><span class=keyword>auto</span> <span class=identifier>first</span><span class=special>=</span><span class=identifier>src</span><span class=special>.</span><span class=identifier>begin</span><span class=special>(),</span><span class=identifier>last</span><span class=special>=</span><span class=identifier>src</span><span class=special>.</span><span class=identifier>end</span><span class=special>();</span><span class=identifier>first</span><span class=special>!=</span><span class=identifier>last</span><span class=special>;){</span>
<span class=keyword>if</span><span class=special>(*</span><span class=identifier>first</span><span class=special>%</span><span class=number>2</span><span class=special>==</span><span class=number>0</span><span class=special>)</span> <span class=identifier>dst</span><span class=special>.</span><span class=identifier>insert</span><span class=special>(</span><span class=identifier>dst</span><span class=special>.</span><span class=identifier>end</span><span class=special>(),</span><span class=identifier>src</span><span class=special>.</span><span class=identifier>extract</span><span class=special>(</span><span class=identifier>first</span><span class=special>++));</span>
<span class=keyword>else</span> <span class=special>++</span><span class=identifier>first</span><span class=special>;</span>
<span class=special>}</span>
</pre></blockquote>
<p>
Note that <code>src</code> and <code>dst</code> are of different types,
yet transfer is possible. Two <code>multi_index_container</code>s are
node-compatible (that is, they use the same <code>node_type</code>) if
they have the same element and allocator types and their respective indices match
one by one without regard to whether they are unique or non-unique or to
their particular configuration parameters: they are both ordered, or
both sequenced, etc.
</p>
<h2><a name="complexity">Complexity and exception safety</a></h2> <h2><a name="complexity">Complexity and exception safety</a></h2>
<p> <p>
@ -1317,9 +1250,9 @@ Index types
<br> <br>
<p>Revised May 9th 2020</p> <p>Revised August 16th 2021</p>
<p>&copy; Copyright 2003-2020 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz. <p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz.
Distributed under the Boost Software Distributed under the Boost Software
License, Version 1.0. (See accompanying file <a href="../../../../LICENSE_1_0.txt"> License, Version 1.0. (See accompanying file <a href="../../../../LICENSE_1_0.txt">
LICENSE_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt"> LICENSE_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt">

View File

@ -55,6 +55,7 @@ Key extraction
</li> </li>
<li><a href="#rearrange">Index rearranging</a></li> <li><a href="#rearrange">Index rearranging</a></li>
<li><a href="#iterator_to"><code>iterator_to</code></a></li> <li><a href="#iterator_to"><code>iterator_to</code></a></li>
<li><a href="#node_handling">Node handling operations</a></li>
<li><a href="#ordered_node_compression">Ordered indices node compression</a></li> <li><a href="#ordered_node_compression">Ordered indices node compression</a></li>
</ul> </ul>
@ -282,7 +283,7 @@ determining whether a hashed index is preferred over an ordered one.
</p> </p>
<p> <p>
Hashed indices replicate the interface as <code>std::unordered_set</code> and Hashed indices replicate the interface of <code>std::unordered_set</code> and
<code>std::unordered_multiset</code>, with only minor differences where required <code>std::unordered_multiset</code>, with only minor differences where required
by the general constraints of <code>multi_index_container</code>s, and provide by the general constraints of <code>multi_index_container</code>s, and provide
additional useful capabilities like in-place updating of elements. additional useful capabilities like in-place updating of elements.
@ -774,6 +775,113 @@ in scenarios where access via iterators is not suitable or desireable:
</ul> </ul>
</p> </p>
<h2><a name="node_handling">Node handling operations</a></h2>
<p>
Using direct node manipulation, elements can be passed between
<code>multi_index_container</code>s without actually copying them:
</p>
<blockquote><pre>
<span class=comment>// move an employee to the retiree archive</span>
<span class=keyword>void</span> <span class=identifier>move_to_retirement</span><span class=special>(</span><span class=keyword>int</span> <span class=identifier>ssnumber</span><span class=special>,</span><span class=identifier>employee_set</span><span class=special>&amp;</span> <span class=identifier>es</span><span class=special>,</span><span class=identifier>employee_set</span><span class=special>&amp;</span> <span class=identifier>archive</span><span class=special>)</span>
<span class=special>{</span>
<span class=comment>// assume employee_set has an index on SS number(not shown before)</span>
<span class=comment>// extract the employee with given SS number to a node handle</span>
<span class=identifier>employee_set_by_ssn</span><span class=special>::</span><span class=identifier>node_type</span> <span class=identifier>node</span><span class=special>=</span><span class=identifier>es</span><span class=special>.</span><span class=identifier>get</span><span class=special>&lt;</span><span class=identifier>ssn</span><span class=special>&gt;().</span><span class=identifier>extract</span><span class=special>(</span><span class=identifier>ssnumber</span><span class=special>);</span>
<span class=keyword>if</span><span class=special>(!</span><span class=identifier>node</span><span class=special>.</span><span class=identifier>empty</span><span class=special>()){</span> <span class=comment>// employee found
// re-insert into archive (note the use of std::move)</span>
<span class=identifier>archive</span><span class=special>.</span><span class=identifier>insert</span><span class=special>(</span><span class=identifier>std</span><span class=special>::</span><span class=identifier>move</span><span class=special>(</span><span class=identifier>node</span><span class=special>));</span>
<span class=special>}</span>
<span class=special>}</span>
</pre></blockquote>
<p>
In the example, the internal node is transferred as-is from <code>es</code> to <code>archive</code>,
which is more efficient than erasing from the source and recreating in destination.
<code>node_type</code> is a move-only class used to pass nodes around, and its interface follows
that of the <a href="https://en.cppreference.com/w/cpp/container/node_handle">homonym type</a>
for C++ associative containers (set containers version). Boost.MultiIndex provides node extraction
and insertion operations for all index types, including sequenced ones (by contrast,
<code>std::list</code> does not have such features):
</p>
<blockquote><pre>
<span class=identifier>multi_index_container</span><span class=special>&lt;</span>
<span class=keyword>int</span><span class=special>,</span>
<span class=identifier>indexed_by</span><span class=special>&lt;</span>
<span class=identifier>sequenced</span><span class=special>&lt;&gt;,</span>
<span class=identifier>ordered_unique</span><span class=special>&lt;</span><span class=identifier>identity</span><span class=special>&lt;</span><span class=keyword>int</span><span class=special>&gt;</span> <span class=special>&gt;</span>
<span class=special>&gt;</span>
<span class=special>&gt;</span> <span class=identifier>src</span><span class=special>;</span>
<span class=identifier>multi_index_container</span><span class=special>&lt;</span>
<span class=keyword>int</span><span class=special>,</span>
<span class=identifier>indexed_by</span><span class=special>&lt;</span>
<span class=identifier>sequenced</span><span class=special>&lt;&gt;,</span>
<span class=identifier>ordered_non_unique</span><span class=special>&lt;</span><span class=identifier>identity</span><span class=special>&lt;</span><span class=keyword>int</span><span class=special>&gt;,</span> <span class=identifier>std</span><span class=special>::</span><span class=identifier>greater</span><span class=special>&lt;</span><span class=keyword>int</span><span class=special>&gt;</span> <span class=special>&gt;</span>
<span class=special>&gt;</span>
<span class=special>&gt;</span> <span class=identifier>dst</span><span class=special>;</span>
<span class=special>...</span>
<span class=comment>// transfer even numbers from src to dst</span>
<span class=keyword>for</span><span class=special>(</span><span class=keyword>auto</span> <span class=identifier>first</span><span class=special>=</span><span class=identifier>src</span><span class=special>.</span><span class=identifier>begin</span><span class=special>(),</span><span class=identifier>last</span><span class=special>=</span><span class=identifier>src</span><span class=special>.</span><span class=identifier>end</span><span class=special>();</span><span class=identifier>first</span><span class=special>!=</span><span class=identifier>last</span><span class=special>;){</span>
<span class=keyword>if</span><span class=special>(*</span><span class=identifier>first</span><span class=special>%</span><span class=number>2</span><span class=special>==</span><span class=number>0</span><span class=special>)</span> <span class=identifier>dst</span><span class=special>.</span><span class=identifier>insert</span><span class=special>(</span><span class=identifier>dst</span><span class=special>.</span><span class=identifier>end</span><span class=special>(),</span><span class=identifier>src</span><span class=special>.</span><span class=identifier>extract</span><span class=special>(</span><span class=identifier>first</span><span class=special>++));</span>
<span class=keyword>else</span> <span class=special>++</span><span class=identifier>first</span><span class=special>;</span>
<span class=special>}</span>
</pre></blockquote>
<p>
Note that <code>src</code> and <code>dst</code> are of different types,
yet transfer is possible. Two <code>multi_index_container</code>s are
node-compatible (that is, they use the same <code>node_type</code>) if
they have the same element and allocator types and their respective indices match
one by one without regard to whether they are unique or non-unique or to
their particular configuration parameters: they are both ordered, or
both sequenced, etc.
</p>
<p>
Alternatively, direct node transfer between two containers can be done without
keeping intervening <code>node_type</code>s thanks to <code>merge</code> (key-based
indices) and <code>splice</code> (non key-based indices).
</p>
<blockquote><pre>
<span class=comment>// move older employees to retirement</span>
<span class=keyword>void</span> <span class=identifier>move_to_retirement_by_age</span><span class=special>(
</span><span class=keyword>int</span> <span class=identifier>max_age</span><span class=special>,</span><span class=identifier>employee_set</span><span class=special>&amp;</span> <span class=identifier>es</span><span class=special>,</span><span class=identifier>employee_set</span><span class=special>&amp;</span> <span class=identifier>archive</span><span class=special>)</span>
<span class=special>{</span>
<span class=comment>// assume employee_set has an index on age (not shown before)</span>
<span class=identifier>employee_set_by_age</span><span class=special>&amp;</span> <span class=identifier>ea</span><span class=special>=</span><span class=identifier>es</span><span class=special>.</span><span class=identifier>get</span><span class=special>&lt;</span><span class=identifier>age</span><span class=special>&gt;();</span>
<span class=comment>// archive employees with age&gt;max_age</span>
<span class=identifier>archive</span><span class=special>.</span><span class=identifier>merge</span><span class=special>(</span><span class=identifier>ea</span><span class=special>,</span><span class=identifier>ea</span><span class=special>.</span><span class=identifier>upper_bound</span><span class=special>(</span><span class=identifier>max_age</span><span class=special>),</span><span class=identifier>ea</span><span class=special>.</span><span class=identifier>end</span><span class=special>());</span>
<span class=special>}</span>
<span class=special>...</span>
<span class=comment>// transfer even numbers from src to dst</span>
<span class=keyword>for</span><span class=special>(</span><span class=keyword>auto</span> <span class=identifier>first</span><span class=special>=</span><span class=identifier>src</span><span class=special>.</span><span class=identifier>begin</span><span class=special>(),</span><span class=identifier>last</span><span class=special>=</span><span class=identifier>src</span><span class=special>.</span><span class=identifier>end</span><span class=special>();</span><span class=identifier>first</span><span class=special>!=</span><span class=identifier>last</span><span class=special>;){</span>
<span class=keyword>if</span><span class=special>(*</span><span class=identifier>first</span><span class=special>%</span><span class=number>2</span><span class=special>==</span><span class=number>0</span><span class=special>)</span> <span class=identifier>dst</span><span class=special>.</span><span class=identifier>splice</span><span class=special>(</span><span class=identifier>dst</span><span class=special>.</span><span class=identifier>end</span><span class=special>(),</span><span class=identifier>src</span><span class=special>,</span><span class=identifier>first</span><span class=special>++);</span>
<span class=keyword>else</span> <span class=special>++</span><span class=identifier>first</span><span class=special>;</span>
<span class=special>}</span>
</pre></blockquote>
<p>
There are overloads of <code>merge</code>/<code>splice</code> for transferring a single element,
a range between two iterators and an entire container: for further details, consult
for instance the reference for <a href="../reference/ord_indices.html#merge">ordered indices</a> and for
<a href="../reference/seq_indices.html#splice">sequenced indices</a>
(the rest of indices provide one interface or the other).
Please note that sequenced and random access indices do also have an operation called <code>merge</code>,
but this follows the specification of <code>std::list::merge</code>, which has a somewhat
different behavior (source and destination are required to be ordered by the same criterion). This is
a rather confusing naming issue that Boost.MultiIndex simply inherits from the C++ standard.
</p>
<h2><a name="ordered_node_compression">Ordered indices node compression</a></h2> <h2><a name="ordered_node_compression">Ordered indices node compression</a></h2>
<p> <p>
@ -821,7 +929,7 @@ Key extraction
<br> <br>
<p>Revised July 29th 2021</p> <p>Revised August 16th 2021</p>
<p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz. <p>&copy; Copyright 2003-2021 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz.
Distributed under the Boost Software Distributed under the Boost Software

View File

@ -0,0 +1,76 @@
/* Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See http://www.boost.org/libs/multi_index for library home page.
*/
#ifndef BOOST_MULTI_INDEX_DETAIL_ANY_CONTAINER_VIEW_HPP
#define BOOST_MULTI_INDEX_DETAIL_ANY_CONTAINER_VIEW_HPP
#if defined(_MSC_VER)
#pragma once
#endif
namespace boost{
namespace multi_index{
namespace detail{
/* type-erased, non-owning view over a ConstIterator-container's range */
template<typename ConstIterator>
class any_container_view
{
public:
template<typename Container>
any_container_view(const Container& x):px(&x),pt(vtable_for<Container>()){}
const void* container()const{return px;}
ConstIterator begin()const{return pt->begin(px);}
ConstIterator end()const{return pt->end(px);}
private:
struct vtable
{
ConstIterator (*begin)(const void*);
ConstIterator (*end)(const void*);
};
template<typename Container>
static ConstIterator begin_for(const void* px)
{
return static_cast<const Container*>(px)->begin();
}
template<typename Container>
static ConstIterator end_for(const void* px)
{
return static_cast<const Container*>(px)->end();
}
template<typename Container>
vtable* vtable_for()
{
static vtable v=
{
&begin_for<Container>,
&end_for<Container>
};
return &v;
}
const void* px;
vtable* pt;
};
} /* namespace multi_index::detail */
} /* namespace multi_index */
} /* namespace boost */
#endif

View File

@ -0,0 +1,74 @@
/* Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See http://www.boost.org/libs/multi_index for library home page.
*/
#ifndef BOOST_MULTI_INDEX_DETAIL_INDEX_ACCESS_SEQUENCE_HPP
#define BOOST_MULTI_INDEX_DETAIL_INDEX_ACCESS_SEQUENCE_HPP
#if defined(_MSC_VER)
#pragma once
#endif
namespace boost{
namespace multi_index{
namespace detail{
/* Successive access to the indices of a multi_index_container. Used as dst in
* backbone function extract_(x,dst) to retrieve the destination indices
* where iterators referring to x must be transferred to (in merging operations).
*/
template<typename MultiIndexContainer,int N=0>
struct index_access_sequence;
struct index_access_sequence_terminal
{
index_access_sequence_terminal(void*){}
};
template<typename MultiIndexContainer,int N>
struct index_access_sequence_normal
{
MultiIndexContainer* p;
index_access_sequence_normal(MultiIndexContainer* p_):p(p_){}
typename nth_index<MultiIndexContainer,N>::type&
get(){return p->template get<N>();}
index_access_sequence<MultiIndexContainer,N+1>
next(){return index_access_sequence<MultiIndexContainer,N+1>(p);}
};
template<typename MultiIndexContainer,int N>
struct index_access_sequence_base:
mpl::if_c<
N<mpl::size<typename MultiIndexContainer::index_type_list>::type::value,
index_access_sequence_normal<MultiIndexContainer,N>,
index_access_sequence_terminal
>
{};
template<typename MultiIndexContainer,int N>
struct index_access_sequence:
index_access_sequence_base<MultiIndexContainer,N>::type
{
typedef typename index_access_sequence_base<
MultiIndexContainer,N>::type super;
index_access_sequence(MultiIndexContainer* p):super(p){}
};
} /* namespace multi_index::detail */
} /* namespace multi_index */
} /* namespace boost */
#endif

View File

@ -1,4 +1,4 @@
/* Copyright 2003-2020 Joaquin M Lopez Munoz. /* Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -22,6 +22,7 @@
#include <boost/multi_index/detail/allocator_traits.hpp> #include <boost/multi_index/detail/allocator_traits.hpp>
#include <boost/multi_index/detail/copy_map.hpp> #include <boost/multi_index/detail/copy_map.hpp>
#include <boost/multi_index/detail/do_not_copy_elements_tag.hpp> #include <boost/multi_index/detail/do_not_copy_elements_tag.hpp>
#include <boost/multi_index/detail/index_access_sequence.hpp>
#include <boost/multi_index/detail/node_handle.hpp> #include <boost/multi_index/detail/node_handle.hpp>
#include <boost/multi_index/detail/node_type.hpp> #include <boost/multi_index/detail/node_type.hpp>
#include <boost/multi_index/detail/vartempl_support.hpp> #include <boost/multi_index/detail/vartempl_support.hpp>
@ -134,6 +135,15 @@ protected:
return x; return x;
} }
template<typename MultiIndexContainer>
final_node_type* insert_(
const value_type&,final_node_type*& x,MultiIndexContainer* p)
{
p->final_extract_for_transfer_(
x,index_access_sequence<final_type>(&final()));
return x;
}
final_node_type* insert_( final_node_type* insert_(
const value_type& v,index_node_type*,final_node_type*& x,lvalue_tag) const value_type& v,index_node_type*,final_node_type*& x,lvalue_tag)
{ {
@ -152,7 +162,8 @@ protected:
return x; return x;
} }
void extract_(index_node_type*){} template<typename Dst>
void extract_(index_node_type*,Dst){}
void clear_(){} void clear_(){}
@ -203,6 +214,10 @@ protected:
final_type& final(){return *static_cast<final_type*>(this);} final_type& final(){return *static_cast<final_type*>(this);}
const final_type& final()const{return *static_cast<const final_type*>(this);} const final_type& final()const{return *static_cast<const final_type*>(this);}
template<typename Index>
static typename Index::final_type& final(Index& x) /* cross-index access */
{return static_cast<typename Index::final_type&>(x);}
final_node_type* final_header()const{return final().header();} final_node_type* final_header()const{return final().header();}
bool final_empty_()const{return final().empty_();} bool final_empty_()const{return final().empty_();}
@ -222,6 +237,10 @@ protected:
std::pair<final_node_type*,bool> final_insert_nh_(final_node_handle_type& nh) std::pair<final_node_type*,bool> final_insert_nh_(final_node_handle_type& nh)
{return final().insert_nh_(nh);} {return final().insert_nh_(nh);}
template<typename Index>
std::pair<final_node_type*,bool> final_transfer_(Index& x,final_node_type* n)
{return final().transfer_(x,n);}
template<BOOST_MULTI_INDEX_TEMPLATE_PARAM_PACK> template<BOOST_MULTI_INDEX_TEMPLATE_PARAM_PACK>
std::pair<final_node_type*,bool> final_emplace_( std::pair<final_node_type*,bool> final_emplace_(
BOOST_MULTI_INDEX_FUNCTION_PARAM_PACK) BOOST_MULTI_INDEX_FUNCTION_PARAM_PACK)
@ -260,12 +279,25 @@ protected:
return final().extract_(x); return final().extract_(x);
} }
template<typename Dst>
void final_extract_for_transfer_(final_node_type* x,Dst dst)
{
final().extract_for_transfer_(x,dst);
}
void final_erase_(final_node_type* x){final().erase_(x);} void final_erase_(final_node_type* x){final().erase_(x);}
void final_delete_node_(final_node_type* x){final().delete_node_(x);} void final_delete_node_(final_node_type* x){final().delete_node_(x);}
void final_delete_all_nodes_(){final().delete_all_nodes_();} void final_delete_all_nodes_(){final().delete_all_nodes_();}
void final_clear_(){final().clear_();} void final_clear_(){final().clear_();}
template<typename Index>
void final_transfer_range_(
Index& x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last)
{final().transfer_range_(x,first,last);}
void final_swap_(final_type& x){final().swap_(x);} void final_swap_(final_type& x){final().swap_(x);}
bool final_replace_( bool final_replace_(

View File

@ -0,0 +1,44 @@
/* Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See http://www.boost.org/libs/multi_index for library home page.
*/
#ifndef BOOST_MULTI_INDEX_DETAIL_INVALIDATE_ITERATORS_HPP
#define BOOST_MULTI_INDEX_DETAIL_INVALIDATE_ITERATORS_HPP
#if defined(_MSC_VER)
#pragma once
#endif
namespace boost{
namespace multi_index{
namespace detail{
/* invalidate_iterators mimics the interface of index_access_sequence (see
* index_access_sequence.hpp) but returns dummy indices whose iterator type
* won't ever match those of the source: the net effect is that safe iterator
* transfer resolves to iterator invalidation, so backbone function invocation
* extract_(x,invalidate_iterators()) is used in extraction scenarios other
* than merging.
*/
struct invalidate_iterators
{
typedef void iterator;
invalidate_iterators& get(){return *this;}
invalidate_iterators& next(){return *this;}
};
} /* namespace multi_index::detail */
} /* namespace multi_index */
} /* namespace boost */
#endif

View File

@ -1,4 +1,4 @@
/* Copyright 2003-2020 Joaquin M Lopez Munoz. /* Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -25,6 +25,12 @@
#include <boost/type_traits/alignment_of.hpp> #include <boost/type_traits/alignment_of.hpp>
#include <new> #include <new>
#if !defined(BOOST_NO_SFINAE)
#include <boost/type_traits/is_const.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/utility/enable_if.hpp>
#endif
namespace boost{ namespace boost{
namespace multi_index{ namespace multi_index{
@ -235,6 +241,23 @@ private:
BOOST_MOVABLE_BUT_NOT_COPYABLE(insert_return_type) BOOST_MOVABLE_BUT_NOT_COPYABLE(insert_return_type)
}; };
/* utility for SFINAEing merge and related operations */
#if !defined(BOOST_NO_SFINAE)
#define BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(Dst,Src,T) \
typename enable_if_c< \
!is_const< Dst >::value&&!is_const< Src >::value&& \
is_same<typename Dst::node_type,typename Src::node_type>::value, \
T \
>::type
#else
#define BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(Dst,Src,T) T
#endif
} /* namespace multi_index::detail */ } /* namespace multi_index::detail */
} /* namespace multi_index */ } /* namespace multi_index */

View File

@ -59,6 +59,7 @@
#include <boost/multi_index/detail/bidir_node_iterator.hpp> #include <boost/multi_index/detail/bidir_node_iterator.hpp>
#include <boost/multi_index/detail/do_not_copy_elements_tag.hpp> #include <boost/multi_index/detail/do_not_copy_elements_tag.hpp>
#include <boost/multi_index/detail/index_node_base.hpp> #include <boost/multi_index/detail/index_node_base.hpp>
#include <boost/multi_index/detail/invalidate_iterators.hpp>
#include <boost/multi_index/detail/modify_key_adaptor.hpp> #include <boost/multi_index/detail/modify_key_adaptor.hpp>
#include <boost/multi_index/detail/node_handle.hpp> #include <boost/multi_index/detail/node_handle.hpp>
#include <boost/multi_index/detail/ord_index_node.hpp> #include <boost/multi_index/detail/ord_index_node.hpp>
@ -127,13 +128,6 @@ template<
> >
class ordered_index_impl: class ordered_index_impl:
BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,public safe_mode::safe_container<
ordered_index_impl<
KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy> >
#endif
{ {
#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\
BOOST_WORKAROUND(__MWERKS__,<=0x3003) BOOST_WORKAROUND(__MWERKS__,<=0x3003)
@ -145,6 +139,12 @@ class ordered_index_impl:
#pragma parse_mfunc_templ off #pragma parse_mfunc_templ off
#endif #endif
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
/* cross-index access */
template <typename,typename,typename> friend class index_base;
#endif
typedef typename SuperMeta::type super; typedef typename SuperMeta::type super;
protected: protected:
@ -171,8 +171,7 @@ public:
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
typedef safe_mode::safe_iterator< typedef safe_mode::safe_iterator<
bidir_node_iterator<index_node_type>, bidir_node_iterator<index_node_type> > iterator;
ordered_index_impl> iterator;
#else #else
typedef bidir_node_iterator<index_node_type> iterator; typedef bidir_node_iterator<index_node_type> iterator;
#endif #endif
@ -222,8 +221,7 @@ protected:
protected: protected:
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
typedef safe_mode::safe_container< typedef safe_mode::safe_container<iterator> safe_container;
ordered_index_impl> safe_super;
#endif #endif
typedef typename call_traits< typedef typename call_traits<
@ -231,11 +229,9 @@ protected:
typedef typename call_traits< typedef typename call_traits<
key_type>::param_type key_param_type; key_type>::param_type key_param_type;
/* Needed to avoid commas in BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL /* needed to avoid commas in some macros */
* expansion.
*/
typedef std::pair<iterator,bool> emplace_return_type; typedef std::pair<iterator,bool> pair_return_type;
public: public:
@ -298,7 +294,7 @@ public:
/* modifiers */ /* modifiers */
BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL(
emplace_return_type,emplace,emplace_impl) pair_return_type,emplace,emplace_impl)
BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG( BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG(
iterator,emplace_hint,emplace_hint_impl,iterator,position) iterator,emplace_hint,emplace_hint_impl,iterator,position)
@ -529,6 +525,75 @@ public:
this->final_clear_(); this->final_clear_();
} }
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(ordered_index_impl,Index,void)
merge(Index& x)
{
merge(x,x.begin(),x.end());
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(ordered_index_impl,Index,void)
merge(BOOST_RV_REF(Index) x){merge(static_cast<Index&>(x));}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(
ordered_index_impl,Index,pair_return_type)
merge(Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i)
{
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(i);
BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,x);
BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x);
BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT;
if(x.end().get_node()==this->header()){ /* same container */
return std::pair<iterator,bool>(
make_iterator(static_cast<final_node_type*>(i.get_node())),true);
}
else{
std::pair<final_node_type*,bool> p=this->final_transfer_(
x,static_cast<final_node_type*>(i.get_node()));
return std::pair<iterator,bool>(make_iterator(p.first),p.second);
}
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(
ordered_index_impl,Index,pair_return_type)
merge(BOOST_RV_REF(Index) x,BOOST_DEDUCED_TYPENAME Index::iterator i)
{
return merge(static_cast<Index&>(x),i);
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(ordered_index_impl,Index,void)
merge(
Index& x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last)
{
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(first);
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(last);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(first,x);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,x);
BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last);
BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x);
BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT;
if(x.end().get_node()!=this->header()){ /* different containers */
this->final_transfer_range_(x,first,last);
}
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(ordered_index_impl,Index,void)
merge(
BOOST_RV_REF(Index) x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last)
{
merge(static_cast<Index&>(x),first,last);
}
/* observers */ /* observers */
key_from_value key_extractor()const{return key;} key_from_value key_extractor()const{return key;}
@ -659,6 +724,11 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
super(args_list.get_tail(),al), super(args_list.get_tail(),al),
key(tuples::get<0>(args_list.get_head())), key(tuples::get<0>(args_list.get_head())),
comp_(tuples::get<1>(args_list.get_head())) comp_(tuples::get<1>(args_list.get_head()))
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,safe(*this)
#endif
{ {
empty_initialize(); empty_initialize();
} }
@ -667,13 +737,13 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
const ordered_index_impl< const ordered_index_impl<
KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy>& x): KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy>& x):
super(x), super(x),
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super(),
#endif
key(x.key), key(x.key),
comp_(x.comp_) comp_(x.comp_)
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,safe(*this)
#endif
{ {
/* Copy ctor just takes the key and compare objects from x. The rest is /* Copy ctor just takes the key and compare objects from x. The rest is
* done in a subsequent call to copy_(). * done in a subsequent call to copy_().
@ -685,13 +755,13 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy>& x, KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy>& x,
do_not_copy_elements_tag): do_not_copy_elements_tag):
super(x,do_not_copy_elements_tag()), super(x,do_not_copy_elements_tag()),
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super(),
#endif
key(x.key), key(x.key),
comp_(x.comp_) comp_(x.comp_)
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,safe(*this)
#endif
{ {
empty_initialize(); empty_initialize();
} }
@ -703,9 +773,9 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
iterator make_iterator(index_node_type* node) iterator make_iterator(index_node_type* node)
{return iterator(node,this);} {return iterator(node,&safe);}
const_iterator make_iterator(index_node_type* node)const const_iterator make_iterator(index_node_type* node)const
{return const_iterator(node,const_cast<ordered_index_impl*>(this));} {return const_iterator(node,const_cast<safe_container*>(&safe));}
#else #else
iterator make_iterator(index_node_type* node){return iterator(node);} iterator make_iterator(index_node_type* node){return iterator(node);}
const_iterator make_iterator(index_node_type* node)const const_iterator make_iterator(index_node_type* node)const
@ -809,14 +879,15 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
return res; return res;
} }
void extract_(index_node_type* x) template<typename Dst>
void extract_(index_node_type* x,Dst dst)
{ {
node_impl_type::rebalance_for_extract( node_impl_type::rebalance_for_extract(
x->impl(),header()->parent(),header()->left(),header()->right()); x->impl(),header()->parent(),header()->left(),header()->right());
super::extract_(x); super::extract_(x,dst.next());
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
detach_iterators(x); transfer_iterators(dst.get(),x);
#endif #endif
} }
@ -831,7 +902,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
empty_initialize(); empty_initialize();
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::detach_dereferenceable_iterators(); safe.detach_dereferenceable_iterators();
#endif #endif
} }
@ -845,7 +916,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
adl_swap(comp_,x.comp_); adl_swap(comp_,x.comp_);
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::swap(x); safe.swap(x.safe);
#endif #endif
super::swap_(x,swap_allocators); super::swap_(x,swap_allocators);
@ -856,7 +927,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy>& x) KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy>& x)
{ {
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::swap(x); safe.swap(x.safe);
#endif #endif
super::swap_elements_(x); super::swap_elements_(x);
@ -898,7 +969,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
b=in_place(x->value(),x,Category()); b=in_place(x->value(),x,Category());
} }
BOOST_CATCH(...){ BOOST_CATCH(...){
extract_(x); extract_(x,invalidate_iterators());
BOOST_RETHROW; BOOST_RETHROW;
} }
BOOST_CATCH_END BOOST_CATCH_END
@ -908,7 +979,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
BOOST_TRY{ BOOST_TRY{
link_info inf; link_info inf;
if(!link_point(key(x->value()),inf,Category())){ if(!link_point(key(x->value()),inf,Category())){
super::extract_(x); super::extract_(x,invalidate_iterators());
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
detach_iterators(x); detach_iterators(x);
@ -918,7 +989,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
node_impl_type::link(x->impl(),inf.side,inf.pos,header()->impl()); node_impl_type::link(x->impl(),inf.side,inf.pos,header()->impl());
} }
BOOST_CATCH(...){ BOOST_CATCH(...){
super::extract_(x); super::extract_(x,invalidate_iterators());
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
detach_iterators(x); detach_iterators(x);
@ -1272,6 +1343,13 @@ private:
iterator it=make_iterator(x); iterator it=make_iterator(x);
safe_mode::detach_equivalent_iterators(it); safe_mode::detach_equivalent_iterators(it);
} }
template<typename Dst>
void transfer_iterators(Dst& dst,index_node_type* x)
{
iterator it=make_iterator(x);
safe_mode::transfer_equivalent_iterators(dst,it);
}
#endif #endif
template<BOOST_MULTI_INDEX_TEMPLATE_PARAM_PACK> template<BOOST_MULTI_INDEX_TEMPLATE_PARAM_PACK>
@ -1449,6 +1527,10 @@ protected: /* for the benefit of AugmentPolicy::augmented_interface */
key_from_value key; key_from_value key;
key_compare comp_; key_compare comp_;
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_container safe;
#endif
#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\
BOOST_WORKAROUND(__MWERKS__,<=0x3003) BOOST_WORKAROUND(__MWERKS__,<=0x3003)
#pragma parse_mfunc_templ reset #pragma parse_mfunc_templ reset

View File

@ -1,4 +1,4 @@
/* Copyright 2003-2018 Joaquin M Lopez Munoz. /* Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -169,6 +169,25 @@ struct random_access_index_node_impl
} }
} }
static ptr_pointer gather_nulls(
ptr_pointer pbegin,ptr_pointer pend,ptr_pointer x)
{
for(ptr_pointer p=pbegin;p!=x;++p){
if(*p){
*pbegin=*p;
(*pbegin)->up()=pbegin;
++pbegin;
}
}
for(ptr_pointer p=pend;p!=x;){
if(*--p){
*--pend=*p;
(*pend)->up()=pend;
}
}
return pbegin;
}
private: private:
ptr_pointer up_; ptr_pointer up_;
}; };

View File

@ -1,4 +1,4 @@
/* Copyright 2003-2020 Joaquin M Lopez Munoz. /* Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -15,9 +15,9 @@
/* Safe mode machinery, in the spirit of Cay Hortmann's "Safe STL" /* Safe mode machinery, in the spirit of Cay Hortmann's "Safe STL"
* (http://www.horstmann.com/safestl.html). * (http://www.horstmann.com/safestl.html).
* In this mode, containers of type Container are derived from * In this mode, containers have to redefine their iterators as
* safe_container<Container>, and their corresponding iterators * safe_iterator<base_iterator> and keep a tracking object member of
* are wrapped with safe_iterator. These classes provide * type safe_container<safe_iterator<base_iterator> >. These classes provide
* an internal record of which iterators are at a given moment associated * an internal record of which iterators are at a given moment associated
* to a given container, and properly mark the iterators as invalid * to a given container, and properly mark the iterators as invalid
* when the container gets destroyed. * when the container gets destroyed.
@ -80,6 +80,11 @@
safe_mode::check_is_owner(it,cont), \ safe_mode::check_is_owner(it,cont), \
safe_mode::not_owner); safe_mode::not_owner);
#define BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,cont) \
BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
safe_mode::check_belongs_in_some_index(it,cont), \
safe_mode::not_owner);
#define BOOST_MULTI_INDEX_CHECK_SAME_OWNER(it0,it1) \ #define BOOST_MULTI_INDEX_CHECK_SAME_OWNER(it0,it1) \
BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \ BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
safe_mode::check_same_owner(it0,it1), \ safe_mode::check_same_owner(it0,it1), \
@ -113,9 +118,12 @@
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
#include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */ #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
#include <algorithm> #include <algorithm>
#include <boost/core/addressof.hpp>
#include <boost/multi_index/detail/access_specifier.hpp> #include <boost/multi_index/detail/access_specifier.hpp>
#include <boost/multi_index/detail/any_container_view.hpp>
#include <boost/multi_index/detail/iter_adaptor.hpp> #include <boost/multi_index/detail/iter_adaptor.hpp>
#include <boost/multi_index/safe_mode_errors.hpp> #include <boost/multi_index/safe_mode_errors.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION) #if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION)
@ -125,6 +133,7 @@
#if defined(BOOST_HAS_THREADS) #if defined(BOOST_HAS_THREADS)
#include <boost/detail/lightweight_mutex.hpp> #include <boost/detail/lightweight_mutex.hpp>
#include <boost/multi_index/detail/scoped_bilock.hpp>
#endif #endif
namespace boost{ namespace boost{
@ -162,18 +171,29 @@ inline bool check_decrementable_iterator(const Iterator& it)
return (it.valid()&&it!=it.owner()->begin())||it.unchecked(); return (it.valid()&&it!=it.owner()->begin())||it.unchecked();
} }
template<typename Iterator> template<typename Iterator,typename Container>
inline bool check_is_owner( inline bool check_is_owner(
const Iterator& it,const typename Iterator::container_type& cont) const Iterator& it,const Container& cont)
{ {
return (it.valid()&&it.owner()==&cont)||it.unchecked(); return (it.valid()&&
it.owner()->container()==cont.end().owner()->container())
||it.unchecked();
} }
template<typename Iterator,typename MultiIndexContainer>
inline bool check_belongs_in_some_index(
const Iterator& it,const MultiIndexContainer& cont)
{
return (it.valid()&&it.owner()->end().get_node()==cont.end().get_node())
||it.unchecked();
}
template<typename Iterator> template<typename Iterator>
inline bool check_same_owner(const Iterator& it0,const Iterator& it1) inline bool check_same_owner(const Iterator& it0,const Iterator& it1)
{ {
return (it0.valid()&&it1.valid()&&it0.owner()==it1.owner())|| return (it0.valid()&&it1.valid()&&
it0.unchecked()||it1.unchecked(); it0.owner()->container()==it1.owner()->container())
||it0.unchecked()||it1.unchecked();
} }
template<typename Iterator> template<typename Iterator>
@ -217,6 +237,28 @@ inline bool check_outside_range(
return true; return true;
} }
template<typename Iterator1,typename Iterator2>
inline bool check_outside_range(
const Iterator1& it,const Iterator2& it0,const Iterator2& it1)
{
if(it.valid()&&it!=it.owner()->end()&&it0.valid()){
Iterator2 last=it0.owner()->end();
bool found=false;
Iterator2 first=it0;
for(;first!=last;++first){
if(first==it1)break;
/* crucial that this check goes after previous break */
if(boost::addressof(*first)==boost::addressof(*it))found=true;
}
if(first!=it1)return false;
return !found;
}
return true;
}
template<typename Iterator,typename Difference> template<typename Iterator,typename Difference>
inline bool check_in_bounds(const Iterator& it,Difference n) inline bool check_in_bounds(const Iterator& it,Difference n)
{ {
@ -233,6 +275,12 @@ inline bool check_different_container(
return &cont0!=&cont1; return &cont0!=&cont1;
} }
template<typename Container1,typename Container2>
inline bool check_different_container(const Container1&,const Container2&)
{
return true;
}
template<typename Container0,typename Container1> template<typename Container0,typename Container1>
inline bool check_equal_allocators( inline bool check_equal_allocators(
const Container0& cont0,const Container1& cont1) const Container0& cont0,const Container1& cont1)
@ -240,42 +288,20 @@ inline bool check_equal_allocators(
return cont0.get_allocator()==cont1.get_allocator(); return cont0.get_allocator()==cont1.get_allocator();
} }
/* Invalidates all iterators equivalent to that given. Safe containers /* fwd decls */
* must call this when deleting elements: the safe mode framework cannot
* perform this operation automatically without outside help.
*/
template<typename Iterator> template<typename Container> class safe_container;
inline void detach_equivalent_iterators(Iterator& it) template<typename Iterator> void detach_equivalent_iterators(Iterator&);
{
if(it.valid()){
{
#if defined(BOOST_HAS_THREADS)
boost::detail::lightweight_mutex::scoped_lock lock(it.cont->mutex);
#endif
Iterator *prev_,*next_; namespace safe_mode_detail{
for(
prev_=static_cast<Iterator*>(&it.cont->header);
(next_=static_cast<Iterator*>(prev_->next))!=0;){
if(next_!=&it&&*next_==it){
prev_->next=next_->next;
next_->cont=0;
}
else prev_=next_;
}
}
it.detach();
}
}
template<typename Container> class safe_container; /* fwd decl. */ /* fwd decls */
} /* namespace multi_index::safe_mode */ class safe_container_base;
template<typename Dst,typename Iterator>
namespace detail{ void transfer_equivalent_iterators(Dst&,Iterator,boost::true_type);
template<typename Dst,typename Iterator>
class safe_container_base; /* fwd decl. */ inline void transfer_equivalent_iterators(Dst&,Iterator&,boost::false_type);
class safe_iterator_base class safe_iterator_base
{ {
@ -325,12 +351,16 @@ protected:
const safe_container_base* owner()const{return cont;} const safe_container_base* owner()const{return cont;}
BOOST_MULTI_INDEX_PRIVATE_IF_MEMBER_TEMPLATE_FRIENDS: BOOST_MULTI_INDEX_PRIVATE_IF_MEMBER_TEMPLATE_FRIENDS:
friend class safe_container_base;
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
template<typename> friend class safe_mode::safe_container; friend class safe_container_base;
template<typename Iterator> friend template<typename>
void safe_mode::detach_equivalent_iterators(Iterator&); friend class safe_mode::safe_container;
template<typename Iterator>
friend void safe_mode::detach_equivalent_iterators(Iterator&);
template<typename Dst,typename Iterator>
friend void safe_mode_detail::transfer_equivalent_iterators(
Dst&,Iterator,boost::true_type);
#endif #endif
inline void attach(safe_container_base* cont_); inline void attach(safe_container_base* cont_);
@ -346,11 +376,14 @@ public:
safe_container_base(){} safe_container_base(){}
BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
friend class safe_iterator_base;
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
template<typename Iterator> friend friend class safe_iterator_base;
void safe_mode::detach_equivalent_iterators(Iterator&); template<typename Iterator>
friend void safe_mode::detach_equivalent_iterators(Iterator&);
template<typename Dst,typename Iterator>
friend void safe_mode_detail::transfer_equivalent_iterators(
Dst&,Iterator,boost::true_type);
#endif #endif
~safe_container_base() ~safe_container_base()
@ -405,41 +438,36 @@ void safe_iterator_base::detach()
} }
} }
} /* namespace multi_index::detail */ } /* namespace multi_index::safe_mode::safe_mode_detail */
namespace safe_mode{
/* In order to enable safe mode on a container: /* In order to enable safe mode on a container:
* - The container must derive from safe_container<container_type>, * - The container must keep a member of type safe_container<iterator>,
* - iterators must be generated via safe_iterator, which adapts a * - iterators must be generated via safe_iterator, which adapts a
* preexistent unsafe iterator class. * preexistent unsafe iterator class. safe_iterators are passed the
* address of the previous safe_container member at construction time.
*/ */
template<typename Container> template<typename Iterator>
class safe_container;
template<typename Iterator,typename Container>
class safe_iterator: class safe_iterator:
public detail::iter_adaptor<safe_iterator<Iterator,Container>,Iterator>, public detail::iter_adaptor<safe_iterator<Iterator>,Iterator>,
public detail::safe_iterator_base public safe_mode_detail::safe_iterator_base
{ {
typedef detail::iter_adaptor<safe_iterator,Iterator> super; typedef detail::iter_adaptor<safe_iterator,Iterator> super;
typedef detail::safe_iterator_base safe_super; typedef safe_mode_detail::safe_iterator_base safe_super;
public: public:
typedef Container container_type;
typedef typename Iterator::reference reference; typedef typename Iterator::reference reference;
typedef typename Iterator::difference_type difference_type; typedef typename Iterator::difference_type difference_type;
safe_iterator(){} safe_iterator(){}
explicit safe_iterator(safe_container<container_type>* cont_): explicit safe_iterator(safe_container<safe_iterator>* cont_):
safe_super(cont_){} safe_super(cont_){}
template<typename T0> template<typename T0>
safe_iterator(const T0& t0,safe_container<container_type>* cont_): safe_iterator(const T0& t0,safe_container<safe_iterator>* cont_):
super(Iterator(t0)),safe_super(cont_){} super(Iterator(t0)),safe_super(cont_){}
template<typename T0,typename T1> template<typename T0,typename T1>
safe_iterator( safe_iterator(
const T0& t0,const T1& t1,safe_container<container_type>* cont_): const T0& t0,const T1& t1,safe_container<safe_iterator>* cont_):
super(Iterator(t0,t1)),safe_super(cont_){} super(Iterator(t0,t1)),safe_super(cont_){}
safe_iterator(const safe_iterator& x):super(x),safe_super(x){} safe_iterator(const safe_iterator& x):super(x),safe_super(x){}
@ -451,12 +479,11 @@ public:
return *this; return *this;
} }
const container_type* owner()const const safe_container<safe_iterator>* owner()const
{ {
return return
static_cast<const container_type*>( static_cast<const safe_container<safe_iterator>*>(
static_cast<const safe_container<container_type>*>( this->safe_super::owner());
this->safe_super::owner()));
} }
/* get_node is not to be used by the user */ /* get_node is not to be used by the user */
@ -547,21 +574,28 @@ private:
#endif #endif
}; };
template<typename Container> template<typename Iterator>
class safe_container:public detail::safe_container_base class safe_container:public safe_mode_detail::safe_container_base
{ {
typedef detail::safe_container_base super; typedef safe_mode_detail::safe_container_base super;
detail::any_container_view<Iterator> view;
public: public:
template<typename Container>
safe_container(const Container& c):view(c){}
const void* container()const{return view.container();}
Iterator begin()const{return view.begin();}
Iterator end()const{return view.end();}
void detach_dereferenceable_iterators() void detach_dereferenceable_iterators()
{ {
typedef typename Container::iterator iterator; Iterator end_=view.end();
Iterator *prev_,*next_;
iterator end_=static_cast<Container*>(this)->end();
iterator *prev_,*next_;
for( for(
prev_=static_cast<iterator*>(&this->header); prev_=static_cast<Iterator*>(&this->header);
(next_=static_cast<iterator*>(prev_->next))!=0;){ (next_=static_cast<Iterator*>(prev_->next))!=0;){
if(*next_!=end_){ if(*next_!=end_){
prev_->next=next_->next; prev_->next=next_->next;
next_->cont=0; next_->cont=0;
@ -570,21 +604,102 @@ public:
} }
} }
void swap(safe_container<Container>& x) void swap(safe_container<Iterator>& x)
{ {
super::swap(x); super::swap(x);
} }
}; };
/* Invalidates all iterators equivalent to that given. Safe containers
* must call this when deleting elements: the safe mode framework cannot
* perform this operation automatically without outside help.
*/
template<typename Iterator>
inline void detach_equivalent_iterators(Iterator& it)
{
if(it.valid()){
{
#if defined(BOOST_HAS_THREADS)
boost::detail::lightweight_mutex::scoped_lock lock(it.cont->mutex);
#endif
Iterator *prev_,*next_;
for(
prev_=static_cast<Iterator*>(&it.cont->header);
(next_=static_cast<Iterator*>(prev_->next))!=0;){
if(next_!=&it&&*next_==it){
prev_->next=next_->next;
next_->cont=0;
}
else prev_=next_;
}
}
it.detach();
}
}
/* Transfers iterators equivalent to that given to Dst, if that container has
* the same iterator type; otherwise, detaches them.
*/
template<typename Dst,typename Iterator>
inline void transfer_equivalent_iterators(Dst& dst,Iterator& i)
{
safe_mode_detail::transfer_equivalent_iterators(
dst,i,boost::is_same<Iterator,typename Dst::iterator>());
}
namespace safe_mode_detail{
template<typename Dst,typename Iterator>
inline void transfer_equivalent_iterators(
Dst& dst,Iterator it,boost::true_type /* same iterator type */)
{
if(it.valid()){
{
safe_container_base* cont_=dst.end().cont;
#if defined(BOOST_HAS_THREADS)
detail::scoped_bilock<boost::detail::lightweight_mutex>
scoped_bilock(it.cont->mutex,cont_->mutex);
#endif
Iterator *prev_,*next_;
for(
prev_=static_cast<Iterator*>(&it.cont->header);
(next_=static_cast<Iterator*>(prev_->next))!=0;){
if(next_!=&it&&*next_==it){
prev_->next=next_->next;
next_->cont=cont_;
next_->next=cont_->header.next;
cont_->header.next=next_;
}
else prev_=next_;
}
}
/* nothing to do with it, was passed by value and will die now */
}
}
template<typename Dst,typename Iterator>
inline void transfer_equivalent_iterators(
Dst&,Iterator& it,boost::false_type /* same iterator type */)
{
detach_equivalent_iterators(it);
}
} /* namespace multi_index::safe_mode::safe_mode_detail */
} /* namespace multi_index::safe_mode */ } /* namespace multi_index::safe_mode */
} /* namespace multi_index */ } /* namespace multi_index */
#if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION) #if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION)
namespace serialization{ namespace serialization{
template<typename Iterator,typename Container> template<typename Iterator>
struct version< struct version<
boost::multi_index::safe_mode::safe_iterator<Iterator,Container> boost::multi_index::safe_mode::safe_iterator<Iterator>
> >
{ {
BOOST_STATIC_CONSTANT( BOOST_STATIC_CONSTANT(

View File

@ -0,0 +1,70 @@
/* Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See http://www.boost.org/libs/multi_index for library home page.
*/
#ifndef BOOST_MULTI_INDEX_DETAIL_SCOPED_BILOCK_HPP
#define BOOST_MULTI_INDEX_DETAIL_SCOPED_BILOCK_HPP
#if defined(_MSC_VER)
#pragma once
#endif
#include <boost/noncopyable.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <functional>
#include <new>
namespace boost{
namespace multi_index{
namespace detail{
/* Locks/unlocks two RAII-lockable mutexes taking care that locking is done in
* a deadlock-avoiding global order and no double locking happens when the two
* mutexes are the same.
*/
template<typename Mutex>
class scoped_bilock:private noncopyable
{
public:
scoped_bilock(Mutex& mutex1,Mutex& mutex2):mutex_eq(&mutex1==&mutex2)
{
bool mutex_lt=std::less<Mutex*>()(&mutex1,&mutex2);
::new (static_cast<void*>(&lock1)) scoped_lock(mutex_lt?mutex1:mutex2);
if(!mutex_eq)
::new (static_cast<void*>(&lock2)) scoped_lock(mutex_lt?mutex2:mutex1);
}
~scoped_bilock()
{
reinterpret_cast<scoped_lock*>(&lock1)->~scoped_lock();
if(!mutex_eq)
reinterpret_cast<scoped_lock*>(&lock2)->~scoped_lock();
}
private:
typedef typename Mutex::scoped_lock scoped_lock;
typedef typename aligned_storage<
sizeof(scoped_lock),
alignment_of<scoped_lock>::value
>::type scoped_lock_space;
bool mutex_eq;
scoped_lock_space lock1,lock2;
};
} /* namespace multi_index::detail */
} /* namespace multi_index */
} /* namespace boost */
#endif

View File

@ -34,6 +34,7 @@
#include <boost/multi_index/detail/do_not_copy_elements_tag.hpp> #include <boost/multi_index/detail/do_not_copy_elements_tag.hpp>
#include <boost/multi_index/detail/hash_index_iterator.hpp> #include <boost/multi_index/detail/hash_index_iterator.hpp>
#include <boost/multi_index/detail/index_node_base.hpp> #include <boost/multi_index/detail/index_node_base.hpp>
#include <boost/multi_index/detail/invalidate_iterators.hpp>
#include <boost/multi_index/detail/modify_key_adaptor.hpp> #include <boost/multi_index/detail/modify_key_adaptor.hpp>
#include <boost/multi_index/detail/node_handle.hpp> #include <boost/multi_index/detail/node_handle.hpp>
#include <boost/multi_index/detail/promotes_arg.hpp> #include <boost/multi_index/detail/promotes_arg.hpp>
@ -88,12 +89,6 @@ template<
> >
class hashed_index: class hashed_index:
BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,public safe_mode::safe_container<
hashed_index<KeyFromValue,Hash,Pred,SuperMeta,TagList,Category> >
#endif
{ {
#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\
BOOST_WORKAROUND(__MWERKS__,<=0x3003) BOOST_WORKAROUND(__MWERKS__,<=0x3003)
@ -105,6 +100,12 @@ class hashed_index:
#pragma parse_mfunc_templ off #pragma parse_mfunc_templ off
#endif #endif
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
/* cross-index access */
template <typename,typename,typename> friend class index_base;
#endif
typedef typename SuperMeta::type super; typedef typename SuperMeta::type super;
protected: protected:
@ -148,8 +149,7 @@ public:
hashed_index_iterator< hashed_index_iterator<
index_node_type,bucket_array_type, index_node_type,bucket_array_type,
Category, Category,
hashed_index_global_iterator_tag>, hashed_index_global_iterator_tag> > iterator;
hashed_index> iterator;
#else #else
typedef hashed_index_iterator< typedef hashed_index_iterator<
index_node_type,bucket_array_type, index_node_type,bucket_array_type,
@ -191,19 +191,16 @@ protected:
private: private:
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
typedef safe_mode::safe_container< typedef safe_mode::safe_container<iterator> safe_container;
hashed_index> safe_super;
#endif #endif
typedef typename call_traits<value_type>::param_type value_param_type; typedef typename call_traits<value_type>::param_type value_param_type;
typedef typename call_traits< typedef typename call_traits<
key_type>::param_type key_param_type; key_type>::param_type key_param_type;
/* Needed to avoid commas in BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL /* needed to avoid commas in some macros */
* expansion.
*/
typedef std::pair<iterator,bool> emplace_return_type; typedef std::pair<iterator,bool> pair_return_type;
public: public:
@ -273,7 +270,7 @@ public:
/* modifiers */ /* modifiers */
BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL(
emplace_return_type,emplace,emplace_impl) pair_return_type,emplace,emplace_impl)
BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG( BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG(
iterator,emplace_hint,emplace_hint_impl,iterator,position) iterator,emplace_hint,emplace_hint_impl,iterator,position)
@ -508,6 +505,73 @@ public:
this->final_swap_(x.final()); this->final_swap_(x.final());
} }
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,void)
merge(Index& x)
{
merge(x,x.begin(),x.end());
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,void)
merge(BOOST_RV_REF(Index) x){merge(static_cast<Index&>(x));}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,pair_return_type)
merge(Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i)
{
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(i);
BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,x);
BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x);
BOOST_MULTI_INDEX_HASHED_INDEX_CHECK_INVARIANT;
if(x.end().get_node()==this->header()){ /* same container */
return std::pair<iterator,bool>(
make_iterator(static_cast<final_node_type*>(i.get_node())),true);
}
else{
std::pair<final_node_type*,bool> p=this->final_transfer_(
x,static_cast<final_node_type*>(i.get_node()));
return std::pair<iterator,bool>(make_iterator(p.first),p.second);
}
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,pair_return_type)
merge(BOOST_RV_REF(Index) x,BOOST_DEDUCED_TYPENAME Index::iterator i)
{
return merge(static_cast<Index&>(x),i);
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,void)
merge(
Index& x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last)
{
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(first);
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(last);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(first,x);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,x);
BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last);
BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x);
BOOST_MULTI_INDEX_HASHED_INDEX_CHECK_INVARIANT;
if(x.end().get_node()!=this->header()){ /* different containers */
this->final_transfer_range_(x,first,last);
}
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,void)
merge(
BOOST_RV_REF(Index) x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last)
{
merge(static_cast<Index&>(x),first,last);
}
/* observers */ /* observers */
key_from_value key_extractor()const{return key;} key_from_value key_extractor()const{return key;}
@ -687,6 +751,11 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
eq_(tuples::get<3>(args_list.get_head())), eq_(tuples::get<3>(args_list.get_head())),
buckets(al,header()->impl(),tuples::get<0>(args_list.get_head())), buckets(al,header()->impl(),tuples::get<0>(args_list.get_head())),
mlf(1.0f) mlf(1.0f)
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,safe(*this)
#endif
{ {
calculate_max_load(); calculate_max_load();
} }
@ -694,17 +763,17 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
hashed_index( hashed_index(
const hashed_index<KeyFromValue,Hash,Pred,SuperMeta,TagList,Category>& x): const hashed_index<KeyFromValue,Hash,Pred,SuperMeta,TagList,Category>& x):
super(x), super(x),
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super(),
#endif
key(x.key), key(x.key),
hash_(x.hash_), hash_(x.hash_),
eq_(x.eq_), eq_(x.eq_),
buckets(x.get_allocator(),header()->impl(),x.buckets.size()), buckets(x.get_allocator(),header()->impl(),x.buckets.size()),
mlf(x.mlf), mlf(x.mlf),
max_load(x.max_load) max_load(x.max_load)
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,safe(*this)
#endif
{ {
/* Copy ctor just takes the internal configuration objects from x. The rest /* Copy ctor just takes the internal configuration objects from x. The rest
* is done in subsequent call to copy_(). * is done in subsequent call to copy_().
@ -715,16 +784,16 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
const hashed_index<KeyFromValue,Hash,Pred,SuperMeta,TagList,Category>& x, const hashed_index<KeyFromValue,Hash,Pred,SuperMeta,TagList,Category>& x,
do_not_copy_elements_tag): do_not_copy_elements_tag):
super(x,do_not_copy_elements_tag()), super(x,do_not_copy_elements_tag()),
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super(),
#endif
key(x.key), key(x.key),
hash_(x.hash_), hash_(x.hash_),
eq_(x.eq_), eq_(x.eq_),
buckets(x.get_allocator(),header()->impl(),0), buckets(x.get_allocator(),header()->impl(),0),
mlf(1.0f) mlf(1.0f)
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,safe(*this)
#endif
{ {
calculate_max_load(); calculate_max_load();
} }
@ -737,12 +806,12 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
iterator make_iterator(index_node_type* node) iterator make_iterator(index_node_type* node)
{ {
return iterator(node,this); return iterator(node,&safe);
} }
const_iterator make_iterator(index_node_type* node)const const_iterator make_iterator(index_node_type* node)const
{ {
return const_iterator(node,const_cast<hashed_index*>(this)); return const_iterator(node,const_cast<safe_container*>(&safe));
} }
#else #else
iterator make_iterator(index_node_type* node) iterator make_iterator(index_node_type* node)
@ -894,13 +963,14 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
return res; return res;
} }
void extract_(index_node_type* x) template<typename Dst>
void extract_(index_node_type* x,Dst dst)
{ {
unlink(x); unlink(x);
super::extract_(x); super::extract_(x,dst.next());
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
detach_iterators(x); transfer_iterators(dst.get(),x);
#endif #endif
} }
@ -944,7 +1014,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
buckets.clear(header()->impl()); buckets.clear(header()->impl());
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::detach_dereferenceable_iterators(); safe.detach_dereferenceable_iterators();
#endif #endif
} }
@ -961,7 +1031,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
std::swap(max_load,x.max_load); std::swap(max_load,x.max_load);
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::swap(x); safe.swap(x.safe);
#endif #endif
super::swap_(x,swap_allocators); super::swap_(x,swap_allocators);
@ -975,7 +1045,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
std::swap(max_load,x.max_load); std::swap(max_load,x.max_load);
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::swap(x); safe.swap(x.safe);
#endif #endif
super::swap_elements_(x); super::swap_elements_(x);
@ -1017,7 +1087,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
b=in_place(x->impl(),key(x->value()),buc); b=in_place(x->impl(),key(x->value()),buc);
} }
BOOST_CATCH(...){ BOOST_CATCH(...){
extract_(x); extract_(x,invalidate_iterators());
BOOST_RETHROW; BOOST_RETHROW;
} }
BOOST_CATCH_END BOOST_CATCH_END
@ -1026,7 +1096,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
BOOST_TRY{ BOOST_TRY{
link_info pos(buckets.at(buc)); link_info pos(buckets.at(buc));
if(!link_point(x->value(),pos)){ if(!link_point(x->value(),pos)){
super::extract_(x); super::extract_(x,invalidate_iterators());
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
detach_iterators(x); detach_iterators(x);
@ -1036,7 +1106,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
link(x,pos); link(x,pos);
} }
BOOST_CATCH(...){ BOOST_CATCH(...){
super::extract_(x); super::extract_(x,invalidate_iterators());
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
detach_iterators(x); detach_iterators(x);
@ -1576,6 +1646,13 @@ private:
iterator it=make_iterator(x); iterator it=make_iterator(x);
safe_mode::detach_equivalent_iterators(it); safe_mode::detach_equivalent_iterators(it);
} }
template<typename Dst>
void transfer_iterators(Dst& dst,index_node_type* x)
{
iterator it=make_iterator(x);
safe_mode::transfer_equivalent_iterators(dst,it);
}
#endif #endif
template<BOOST_MULTI_INDEX_TEMPLATE_PARAM_PACK> template<BOOST_MULTI_INDEX_TEMPLATE_PARAM_PACK>
@ -1696,7 +1773,11 @@ private:
bucket_array_type buckets; bucket_array_type buckets;
float mlf; float mlf;
size_type max_load; size_type max_load;
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_container safe;
#endif
#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\
BOOST_WORKAROUND(__MWERKS__,<=0x3003) BOOST_WORKAROUND(__MWERKS__,<=0x3003)
#pragma parse_mfunc_templ reset #pragma parse_mfunc_templ reset

View File

@ -1,4 +1,4 @@
/* Copyright 2003-2020 Joaquin M Lopez Munoz. /* Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -42,6 +42,7 @@
#include <boost/multi_index/random_access_index_fwd.hpp> #include <boost/multi_index/random_access_index_fwd.hpp>
#include <boost/throw_exception.hpp> #include <boost/throw_exception.hpp>
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
#include <boost/type_traits/is_copy_constructible.hpp>
#include <boost/type_traits/is_integral.hpp> #include <boost/type_traits/is_integral.hpp>
#include <functional> #include <functional>
#include <stdexcept> #include <stdexcept>
@ -80,12 +81,6 @@ namespace detail{
template<typename SuperMeta,typename TagList> template<typename SuperMeta,typename TagList>
class random_access_index: class random_access_index:
BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,public safe_mode::safe_container<
random_access_index<SuperMeta,TagList> >
#endif
{ {
#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\
BOOST_WORKAROUND(__MWERKS__,<=0x3003) BOOST_WORKAROUND(__MWERKS__,<=0x3003)
@ -97,6 +92,12 @@ class random_access_index:
#pragma parse_mfunc_templ off #pragma parse_mfunc_templ off
#endif #endif
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
/* cross-index access */
template <typename,typename,typename> friend class index_base;
#endif
typedef typename SuperMeta::type super; typedef typename SuperMeta::type super;
protected: protected:
@ -120,8 +121,7 @@ public:
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
typedef safe_mode::safe_iterator< typedef safe_mode::safe_iterator<
rnd_node_iterator<index_node_type>, rnd_node_iterator<index_node_type> > iterator;
random_access_index> iterator;
#else #else
typedef rnd_node_iterator<index_node_type> iterator; typedef rnd_node_iterator<index_node_type> iterator;
#endif #endif
@ -168,18 +168,15 @@ protected:
private: private:
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
typedef safe_mode::safe_container< typedef safe_mode::safe_container<iterator> safe_container;
random_access_index> safe_super;
#endif #endif
typedef typename call_traits< typedef typename call_traits<
value_type>::param_type value_param_type; value_type>::param_type value_param_type;
/* Needed to avoid commas in BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL /* needed to avoid commas in some macros */
* expansion.
*/
typedef std::pair<iterator,bool> emplace_return_type; typedef std::pair<iterator,bool> pair_return_type;
public: public:
@ -325,7 +322,7 @@ public:
/* modifiers */ /* modifiers */
BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL(
emplace_return_type,emplace_front,emplace_front_impl) pair_return_type,emplace_front,emplace_front_impl)
std::pair<iterator,bool> push_front(const value_type& x) std::pair<iterator,bool> push_front(const value_type& x)
{return insert(begin(),x);} {return insert(begin(),x);}
@ -334,7 +331,7 @@ public:
void pop_front(){erase(begin());} void pop_front(){erase(begin());}
BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL(
emplace_return_type,emplace_back,emplace_back_impl) pair_return_type,emplace_back,emplace_back_impl)
std::pair<iterator,bool> push_back(const value_type& x) std::pair<iterator,bool> push_back(const value_type& x)
{return insert(end(),x);} {return insert(end(),x);}
@ -343,7 +340,7 @@ public:
void pop_back(){erase(--end());} void pop_back(){erase(--end());}
BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG( BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG(
emplace_return_type,emplace,emplace_impl,iterator,position) pair_return_type,emplace,emplace_impl,iterator,position)
std::pair<iterator,bool> insert(iterator position,const value_type& x) std::pair<iterator,bool> insert(iterator position,const value_type& x)
{ {
@ -525,33 +522,36 @@ public:
/* list operations */ /* list operations */
void splice(iterator position,random_access_index<SuperMeta,TagList>& x) template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(random_access_index,Index,void)
splice(iterator position,Index& x)
{ {
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this);
BOOST_MULTI_INDEX_CHECK_DIFFERENT_CONTAINER(*this,x);
BOOST_MULTI_INDEX_RND_INDEX_CHECK_INVARIANT; BOOST_MULTI_INDEX_RND_INDEX_CHECK_INVARIANT;
iterator first=x.begin(),last=x.end(); if(x.end().get_node()==this->header()){ /* same container */
size_type n=0; BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(
BOOST_TRY{ position==end(),safe_mode::inside_range);
while(first!=last){
if(push_back(*first).second){
first=x.erase(first);
++n;
}
else ++first;
}
} }
BOOST_CATCH(...){ else{
relocate(position,end()-n,end()); external_splice(
BOOST_RETHROW; position,x,x.begin(),x.end(),
boost::is_copy_constructible<value_type>());
} }
BOOST_CATCH_END
relocate(position,end()-n,end());
} }
void splice( template<typename Index>
iterator position,random_access_index<SuperMeta,TagList>& x,iterator i) BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(random_access_index,Index,void)
splice(iterator position,BOOST_RV_REF(Index) x)
{
splice(position,static_cast<Index&>(x));
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(
random_access_index,Index,pair_return_type)
splice(
iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i)
{ {
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this);
@ -559,28 +559,36 @@ public:
BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i); BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,x); BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,x);
BOOST_MULTI_INDEX_RND_INDEX_CHECK_INVARIANT; BOOST_MULTI_INDEX_RND_INDEX_CHECK_INVARIANT;
if(&x==this)relocate(position,i); if(x.end().get_node()==this->header()){ /* same container */
index_node_type* pn=position.get_node();
index_node_type* in=static_cast<index_node_type*>(i.get_node());
if(pn!=in)relocate(pn,in);
return std::pair<iterator,bool>(make_iterator(in),true);
}
else{ else{
if(insert(position,*i).second){ std::pair<final_node_type*,bool> p=
external_splice(
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) position,x,i,boost::is_copy_constructible<value_type>());
/* MSVC++ 6.0 optimizer has a hard time with safe mode, and the following return std::pair<iterator,bool>(make_iterator(p.first),p.second);
* workaround is needed. Left it for all compilers as it does no
* harm.
*/
i.detach();
x.erase(x.make_iterator(i.get_node()));
#else
x.erase(i);
#endif
}
} }
} }
void splice( template<typename Index>
iterator position,random_access_index<SuperMeta,TagList>& x, BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(
iterator first,iterator last) random_access_index,Index,pair_return_type)
splice(
iterator position,BOOST_RV_REF(Index) x,
BOOST_DEDUCED_TYPENAME Index::iterator i)
{
return splice(position,static_cast<Index&>(x),i);
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(random_access_index,Index,void)
splice(
iterator position,Index& x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last)
{ {
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this);
@ -590,25 +598,24 @@ public:
BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,x); BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,x);
BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last); BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last);
BOOST_MULTI_INDEX_RND_INDEX_CHECK_INVARIANT; BOOST_MULTI_INDEX_RND_INDEX_CHECK_INVARIANT;
if(&x==this)relocate(position,first,last); if(x.end().get_node()==this->header()){ /* same container */
else{ BOOST_MULTI_INDEX_CHECK_OUTSIDE_RANGE(position,first,last);
size_type n=0; internal_splice(position,first,last);
BOOST_TRY{
while(first!=last){
if(push_back(*first).second){
first=x.erase(first);
++n;
}
else ++first;
}
}
BOOST_CATCH(...){
relocate(position,end()-n,end());
BOOST_RETHROW;
}
BOOST_CATCH_END
relocate(position,end()-n,end());
} }
else{
external_splice(
position,x,first,last,boost::is_copy_constructible<value_type>());
}
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(random_access_index,Index,void)
splice(
iterator position,BOOST_RV_REF(Index) x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last)
{
splice(position,static_cast<Index&>(x),first,last);
} }
void remove(value_param_type value) void remove(value_param_type value)
@ -745,17 +752,22 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
const ctor_args_list& args_list,const allocator_type& al): const ctor_args_list& args_list,const allocator_type& al):
super(args_list.get_tail(),al), super(args_list.get_tail(),al),
ptrs(al,header()->impl(),0) ptrs(al,header()->impl(),0)
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,safe(*this)
#endif
{ {
} }
random_access_index(const random_access_index<SuperMeta,TagList>& x): random_access_index(const random_access_index<SuperMeta,TagList>& x):
super(x), super(x),
ptrs(x.get_allocator(),header()->impl(),x.size())
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super(), ,safe(*this)
#endif #endif
ptrs(x.get_allocator(),header()->impl(),x.size())
{ {
/* The actual copying takes place in subsequent call to copy_(). /* The actual copying takes place in subsequent call to copy_().
*/ */
@ -764,12 +776,12 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
random_access_index( random_access_index(
const random_access_index<SuperMeta,TagList>& x,do_not_copy_elements_tag): const random_access_index<SuperMeta,TagList>& x,do_not_copy_elements_tag):
super(x,do_not_copy_elements_tag()), super(x,do_not_copy_elements_tag()),
ptrs(x.get_allocator(),header()->impl(),0)
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super(), ,safe(*this)
#endif #endif
ptrs(x.get_allocator(),header()->impl(),0)
{ {
} }
@ -780,9 +792,9 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
iterator make_iterator(index_node_type* node) iterator make_iterator(index_node_type* node)
{return iterator(node,this);} {return iterator(node,&safe);}
const_iterator make_iterator(index_node_type* node)const const_iterator make_iterator(index_node_type* node)const
{return const_iterator(node,const_cast<random_access_index*>(this));} {return const_iterator(node,const_cast<safe_container*>(&safe));}
#else #else
iterator make_iterator(index_node_type* node){return iterator(node);} iterator make_iterator(index_node_type* node){return iterator(node);}
const_iterator make_iterator(index_node_type* node)const const_iterator make_iterator(index_node_type* node)const
@ -828,13 +840,14 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
return res; return res;
} }
void extract_(index_node_type* x) template<typename Dst>
void extract_(index_node_type* x,Dst dst)
{ {
ptrs.erase(x->impl()); ptrs.erase(x->impl());
super::extract_(x); super::extract_(x,dst.next());
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
detach_iterators(x); transfer_iterators(dst.get(),x);
#endif #endif
} }
@ -852,7 +865,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
ptrs.clear(); ptrs.clear();
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::detach_dereferenceable_iterators(); safe.detach_dereferenceable_iterators();
#endif #endif
} }
@ -863,7 +876,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
ptrs.swap(x.ptrs,swap_allocators); ptrs.swap(x.ptrs,swap_allocators);
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::swap(x); safe.swap(x.safe);
#endif #endif
super::swap_(x,swap_allocators); super::swap_(x,swap_allocators);
@ -874,7 +887,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
ptrs.swap(x.ptrs); ptrs.swap(x.ptrs);
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::swap(x); safe.swap(x.safe);
#endif #endif
super::swap_elements_(x); super::swap_elements_(x);
@ -1000,6 +1013,13 @@ private:
iterator it=make_iterator(x); iterator it=make_iterator(x);
safe_mode::detach_equivalent_iterators(it); safe_mode::detach_equivalent_iterators(it);
} }
template<typename Dst>
void transfer_iterators(Dst& dst,index_node_type* x)
{
iterator it=make_iterator(x);
safe_mode::transfer_equivalent_iterators(dst,it);
}
#endif #endif
template <class InputIterator> template <class InputIterator>
@ -1085,7 +1105,121 @@ private:
return std::pair<iterator,bool>(make_iterator(p.first),p.second); return std::pair<iterator,bool>(make_iterator(p.first),p.second);
} }
ptr_array ptrs; template<typename Index>
std::pair<final_node_type*,bool> external_splice(
iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i,
boost::true_type /* copy-constructible value */)
{
if(get_allocator()==x.get_allocator()){
return external_splice(position,x,i,boost::false_type());
}
else{
/* backwards compatibility with old, non-transfer-based splice */
std::pair<iterator,bool> p=insert(position,*i);
if(p.second)x.erase(i);
return std::pair<final_node_type*,bool>(
static_cast<final_node_type*>(p.first.get_node()),p.second);
}
}
template<typename Index>
std::pair<final_node_type*,bool> external_splice(
iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i,
boost::false_type /* copy-constructible value */)
{
BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x);
std::pair<final_node_type*,bool> p=this->final_transfer_(
x,static_cast<final_node_type*>(i.get_node()));
if(p.second&&position.get_node()!=header()){
relocate(position.get_node(),p.first);
}
return p;
}
template<typename Iterator>
void internal_splice(iterator position,Iterator first,Iterator last)
{
/* null out [first, last) positions in ptrs array */
for(Iterator it=first;it!=last;++it){
*(static_cast<index_node_type*>(it.get_node())->up())=0;
}
node_impl_ptr_pointer pp=node_impl_type::gather_nulls(
ptrs.begin(),ptrs.end(),
static_cast<index_node_type*>(position.get_node())->up());
/* relink [first, last) */
for(Iterator it=first;it!=last;++it,++pp){
*pp=static_cast<index_node_type*>(it.get_node());
(*pp)->up()=pp;
}
}
void internal_splice(iterator position,iterator first,iterator last)
{
index_node_type* pn=position.get_node();
index_node_type* fn=static_cast<index_node_type*>(first.get_node());
index_node_type* ln=static_cast<index_node_type*>(last.get_node());
if(pn!=ln)relocate(pn,fn,ln);
}
template<typename Index>
void external_splice(
iterator position,Index& x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last,
boost::true_type /* copy-constructible value */)
{
if(get_allocator()==x.get_allocator()){
external_splice(position,x,first,last,boost::false_type());
}
else{
/* backwards compatibility with old, non-transfer-based splice */
size_type n=size();
BOOST_TRY{
while(first!=last){
if(push_back(*first).second)first=x.erase(first);
else ++first;
}
}
BOOST_CATCH(...){
relocate(position,begin()+n,end());
BOOST_RETHROW;
}
BOOST_CATCH_END
relocate(position,begin()+n,end());
}
}
template<typename Index>
void external_splice(
iterator position,Index& x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last,
boost::false_type /* copy-constructible value */)
{
BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x);
size_type n=size();
BOOST_TRY{
this->final_transfer_range_(x,first,last);
}
BOOST_CATCH(...){
relocate(position,begin()+n,end());
BOOST_RETHROW;
}
BOOST_CATCH_END
relocate(position,begin()+n,end());
}
ptr_array ptrs;
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_container safe;
#endif
#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\
BOOST_WORKAROUND(__MWERKS__,<=0x3003) BOOST_WORKAROUND(__MWERKS__,<=0x3003)

View File

@ -1,4 +1,4 @@
/* Copyright 2003-2020 Joaquin M Lopez Munoz. /* Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -39,6 +39,7 @@
#include <boost/multi_index/detail/vartempl_support.hpp> #include <boost/multi_index/detail/vartempl_support.hpp>
#include <boost/multi_index/sequenced_index_fwd.hpp> #include <boost/multi_index/sequenced_index_fwd.hpp>
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
#include <boost/type_traits/is_copy_constructible.hpp>
#include <boost/type_traits/is_integral.hpp> #include <boost/type_traits/is_integral.hpp>
#include <functional> #include <functional>
#include <utility> #include <utility>
@ -70,12 +71,6 @@ namespace detail{
template<typename SuperMeta,typename TagList> template<typename SuperMeta,typename TagList>
class sequenced_index: class sequenced_index:
BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,public safe_mode::safe_container<
sequenced_index<SuperMeta,TagList> >
#endif
{ {
#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\
BOOST_WORKAROUND(__MWERKS__,<=0x3003) BOOST_WORKAROUND(__MWERKS__,<=0x3003)
@ -87,6 +82,12 @@ class sequenced_index:
#pragma parse_mfunc_templ off #pragma parse_mfunc_templ off
#endif #endif
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
/* cross-index access */
template <typename,typename,typename> friend class index_base;
#endif
typedef typename SuperMeta::type super; typedef typename SuperMeta::type super;
protected: protected:
@ -107,8 +108,7 @@ public:
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
typedef safe_mode::safe_iterator< typedef safe_mode::safe_iterator<
bidir_node_iterator<index_node_type>, bidir_node_iterator<index_node_type> > iterator;
sequenced_index> iterator;
#else #else
typedef bidir_node_iterator<index_node_type> iterator; typedef bidir_node_iterator<index_node_type> iterator;
#endif #endif
@ -155,17 +155,14 @@ protected:
private: private:
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
typedef safe_mode::safe_container< typedef safe_mode::safe_container<iterator> safe_container;
sequenced_index> safe_super;
#endif #endif
typedef typename call_traits<value_type>::param_type value_param_type; typedef typename call_traits<value_type>::param_type value_param_type;
/* Needed to avoid commas in BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL /* needed to avoid commas in some macros */
* expansion.
*/
typedef std::pair<iterator,bool> emplace_return_type; typedef std::pair<iterator,bool> pair_return_type;
public: public:
@ -287,7 +284,7 @@ public:
/* modifiers */ /* modifiers */
BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL(
emplace_return_type,emplace_front,emplace_front_impl) pair_return_type,emplace_front,emplace_front_impl)
std::pair<iterator,bool> push_front(const value_type& x) std::pair<iterator,bool> push_front(const value_type& x)
{return insert(begin(),x);} {return insert(begin(),x);}
@ -296,7 +293,7 @@ public:
void pop_front(){erase(begin());} void pop_front(){erase(begin());}
BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL(
emplace_return_type,emplace_back,emplace_back_impl) pair_return_type,emplace_back,emplace_back_impl)
std::pair<iterator,bool> push_back(const value_type& x) std::pair<iterator,bool> push_back(const value_type& x)
{return insert(end(),x);} {return insert(end(),x);}
@ -305,7 +302,7 @@ public:
void pop_back(){erase(--end());} void pop_back(){erase(--end());}
BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG( BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG(
emplace_return_type,emplace,emplace_impl,iterator,position) pair_return_type,emplace,emplace_impl,iterator,position)
std::pair<iterator,bool> insert(iterator position,const value_type& x) std::pair<iterator,bool> insert(iterator position,const value_type& x)
{ {
@ -476,20 +473,36 @@ public:
/* list operations */ /* list operations */
void splice(iterator position,sequenced_index<SuperMeta,TagList>& x) template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(sequenced_index,Index,void)
splice(iterator position,Index& x)
{ {
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this);
BOOST_MULTI_INDEX_CHECK_DIFFERENT_CONTAINER(*this,x);
BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT;
iterator first=x.begin(),last=x.end(); if(x.end().get_node()==this->header()){ /* same container */
while(first!=last){ BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(
if(insert(position,*first).second)first=x.erase(first); position==end(),safe_mode::inside_range);
else ++first; }
else{
external_splice(
position,x,x.begin(),x.end(),
boost::is_copy_constructible<value_type>());
} }
} }
void splice(iterator position,sequenced_index<SuperMeta,TagList>& x,iterator i) template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(sequenced_index,Index,void)
splice(iterator position,BOOST_RV_REF(Index) x)
{
splice(position,static_cast<Index&>(x));
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(
sequenced_index,Index,pair_return_type)
splice(
iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i)
{ {
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this);
@ -497,30 +510,36 @@ public:
BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i); BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,x); BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,x);
BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT;
if(&x==this){ if(x.end().get_node()==this->header()){ /* same container */
if(position!=i)relink(position.get_node(),i.get_node()); index_node_type* pn=position.get_node();
index_node_type* in=static_cast<index_node_type*>(i.get_node());
if(pn!=in)relink(pn,in);
return std::pair<iterator,bool>(make_iterator(in),true);
} }
else{ else{
if(insert(position,*i).second){ std::pair<final_node_type*,bool> p=
external_splice(
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) position,x,i,boost::is_copy_constructible<value_type>());
/* MSVC++ 6.0 optimizer has a hard time with safe mode, and the following return std::pair<iterator,bool>(make_iterator(p.first),p.second);
* workaround is needed. Left it for all compilers as it does no
* harm.
*/
i.detach();
x.erase(x.make_iterator(i.get_node()));
#else
x.erase(i);
#endif
}
} }
} }
void splice( template<typename Index>
iterator position,sequenced_index<SuperMeta,TagList>& x, BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(
iterator first,iterator last) sequenced_index,Index,pair_return_type)
splice(
iterator position,BOOST_RV_REF(Index) x,
BOOST_DEDUCED_TYPENAME Index::iterator i)
{
return splice(position,static_cast<Index&>(x),i);
}
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(sequenced_index,Index,void)
splice(
iterator position,Index& x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last)
{ {
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position);
BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this);
@ -530,19 +549,26 @@ public:
BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,x); BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,x);
BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last); BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last);
BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT;
if(&x==this){ if(x.end().get_node()==this->header()){ /* same container */
BOOST_MULTI_INDEX_CHECK_OUTSIDE_RANGE(position,first,last); BOOST_MULTI_INDEX_CHECK_OUTSIDE_RANGE(position,first,last);
if(position!=last)relink( internal_splice(position,first,last);
position.get_node(),first.get_node(),last.get_node());
} }
else{ else{
while(first!=last){ external_splice(
if(insert(position,*first).second)first=x.erase(first); position,x,first,last,boost::is_copy_constructible<value_type>());
else ++first;
}
} }
} }
template<typename Index>
BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(sequenced_index,Index,void)
splice(
iterator position,BOOST_RV_REF(Index) x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last)
{
splice(position,static_cast<Index&>(x),first,last);
}
void remove(value_param_type value) void remove(value_param_type value)
{ {
sequenced_index_remove( sequenced_index_remove(
@ -640,6 +666,11 @@ public:
BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
sequenced_index(const ctor_args_list& args_list,const allocator_type& al): sequenced_index(const ctor_args_list& args_list,const allocator_type& al):
super(args_list.get_tail(),al) super(args_list.get_tail(),al)
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,safe(*this)
#endif
{ {
empty_initialize(); empty_initialize();
} }
@ -648,7 +679,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
super(x) super(x)
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,safe_super() ,safe(*this)
#endif #endif
{ {
@ -660,7 +691,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
super(x,do_not_copy_elements_tag()) super(x,do_not_copy_elements_tag())
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
,safe_super() ,safe(*this)
#endif #endif
{ {
@ -674,9 +705,9 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
iterator make_iterator(index_node_type* node) iterator make_iterator(index_node_type* node)
{return iterator(node,this);} {return iterator(node,&safe);}
const_iterator make_iterator(index_node_type* node)const const_iterator make_iterator(index_node_type* node)const
{return const_iterator(node,const_cast<sequenced_index*>(this));} {return const_iterator(node,const_cast<safe_container*>(&safe));}
#else #else
iterator make_iterator(index_node_type* node){return iterator(node);} iterator make_iterator(index_node_type* node){return iterator(node);}
const_iterator make_iterator(index_node_type* node)const const_iterator make_iterator(index_node_type* node)const
@ -720,13 +751,14 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
return res; return res;
} }
void extract_(index_node_type* x) template<typename Dst>
void extract_(index_node_type* x,Dst dst)
{ {
unlink(x); unlink(x);
super::extract_(x); super::extract_(x,dst.next());
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
detach_iterators(x); transfer_iterators(dst.get(),x);
#endif #endif
} }
@ -746,7 +778,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
empty_initialize(); empty_initialize();
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::detach_dereferenceable_iterators(); safe.detach_dereferenceable_iterators();
#endif #endif
} }
@ -755,7 +787,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
sequenced_index<SuperMeta,TagList>& x,BoolConstant swap_allocators) sequenced_index<SuperMeta,TagList>& x,BoolConstant swap_allocators)
{ {
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::swap(x); safe.swap(x.safe);
#endif #endif
super::swap_(x,swap_allocators); super::swap_(x,swap_allocators);
@ -764,7 +796,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
void swap_elements_(sequenced_index<SuperMeta,TagList>& x) void swap_elements_(sequenced_index<SuperMeta,TagList>& x)
{ {
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_super::swap(x); safe.swap(x.safe);
#endif #endif
super::swap_elements_(x); super::swap_elements_(x);
@ -909,6 +941,13 @@ private:
iterator it=make_iterator(x); iterator it=make_iterator(x);
safe_mode::detach_equivalent_iterators(it); safe_mode::detach_equivalent_iterators(it);
} }
template<typename Dst>
void transfer_iterators(Dst& dst,index_node_type* x)
{
iterator it=make_iterator(x);
safe_mode::transfer_equivalent_iterators(dst,it);
}
#endif #endif
template <class InputIterator> template <class InputIterator>
@ -978,6 +1017,106 @@ private:
return std::pair<iterator,bool>(make_iterator(p.first),p.second); return std::pair<iterator,bool>(make_iterator(p.first),p.second);
} }
template<typename Index>
std::pair<final_node_type*,bool> external_splice(
iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i,
boost::true_type /* copy-constructible value */)
{
if(get_allocator()==x.get_allocator()){
return external_splice(position,x,i,boost::false_type());
}
else{
/* backwards compatibility with old, non-transfer-based splice */
std::pair<iterator,bool> p=insert(position,*i);
if(p.second)x.erase(i);
return std::pair<final_node_type*,bool>(
static_cast<final_node_type*>(p.first.get_node()),p.second);
}
}
template<typename Index>
std::pair<final_node_type*,bool> external_splice(
iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i,
boost::false_type /* copy-constructible value */)
{
BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x);
std::pair<final_node_type*,bool> p=this->final_transfer_(
x,static_cast<final_node_type*>(i.get_node()));
if(p.second&&position.get_node()!=header()){
relink(position.get_node(),p.first);
}
return p;
}
template<typename Iterator>
void internal_splice(iterator position,Iterator first,Iterator last)
{
index_node_type* pn=position.get_node();
while(first!=last){
relink(pn,static_cast<index_node_type*>((first++).get_node()));
}
}
void internal_splice(iterator position,iterator first,iterator last)
{
index_node_type* pn=position.get_node();
index_node_type* fn=static_cast<index_node_type*>(first.get_node());
index_node_type* ln=static_cast<index_node_type*>(last.get_node());
if(pn!=ln)relink(pn,fn,ln);
}
template<typename Index>
void external_splice(
iterator position,Index& x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last,
boost::true_type /* copy-constructible value */)
{
if(get_allocator()==x.get_allocator()){
external_splice(position,x,first,last,boost::false_type());
}
else{
/* backwards compatibility with old, non-transfer-based splice */
while(first!=last){
if(insert(position,*first).second)first=x.erase(first);
else ++first;
}
}
}
template<typename Index>
void external_splice(
iterator position,Index& x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last,
boost::false_type /* copy-constructible value */)
{
BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x);
if(position==end()){
this->final_transfer_range_(x,first,last);
}
else{
iterator first_to_relink=end();
--first_to_relink;
BOOST_TRY{
this->final_transfer_range_(x,first,last);
}
BOOST_CATCH(...){
++first_to_relink;
relink(position.get_node(),first_to_relink.get_node(),header());
}
BOOST_CATCH_END
++first_to_relink;
relink(position.get_node(),first_to_relink.get_node(),header());
}
}
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
safe_container safe;
#endif
#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\
BOOST_WORKAROUND(__MWERKS__,<=0x3003) BOOST_WORKAROUND(__MWERKS__,<=0x3003)
#pragma parse_mfunc_templ reset #pragma parse_mfunc_templ reset

View File

@ -1,6 +1,6 @@
/* Multiply indexed container. /* Multiply indexed container.
* *
* Copyright 2003-2020 Joaquin M Lopez Munoz. * Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -38,6 +38,7 @@
#include <boost/multi_index/detail/converter.hpp> #include <boost/multi_index/detail/converter.hpp>
#include <boost/multi_index/detail/header_holder.hpp> #include <boost/multi_index/detail/header_holder.hpp>
#include <boost/multi_index/detail/has_tag.hpp> #include <boost/multi_index/detail/has_tag.hpp>
#include <boost/multi_index/detail/invalidate_iterators.hpp>
#include <boost/multi_index/detail/no_duplicate_tags.hpp> #include <boost/multi_index/detail/no_duplicate_tags.hpp>
#include <boost/multi_index/detail/safe_mode.hpp> #include <boost/multi_index/detail/safe_mode.hpp>
#include <boost/multi_index/detail/scope_guard.hpp> #include <boost/multi_index/detail/scope_guard.hpp>
@ -489,9 +490,7 @@ public:
#endif #endif
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it);
BOOST_MULTI_INDEX_CHECK_IS_OWNER( BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,*this);
it,static_cast<typename IteratorType::container_type&>(*this));
return index_type::make_iterator( return index_type::make_iterator(
static_cast<final_node_type*>(it.get_node())); static_cast<final_node_type*>(it.get_node()));
} }
@ -508,8 +507,7 @@ public:
#endif #endif
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it);
BOOST_MULTI_INDEX_CHECK_IS_OWNER( BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,*this);
it,static_cast<const typename IteratorType::container_type&>(*this));
return index_type::make_iterator( return index_type::make_iterator(
static_cast<final_node_type*>(it.get_node())); static_cast<final_node_type*>(it.get_node()));
} }
@ -541,8 +539,7 @@ public:
#endif #endif
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it);
BOOST_MULTI_INDEX_CHECK_IS_OWNER( BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,*this);
it,static_cast<typename IteratorType::container_type&>(*this));
return index_type::make_iterator( return index_type::make_iterator(
static_cast<final_node_type*>(it.get_node())); static_cast<final_node_type*>(it.get_node()));
} }
@ -559,8 +556,7 @@ public:
#endif #endif
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it);
BOOST_MULTI_INDEX_CHECK_IS_OWNER( BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,*this);
it,static_cast<const typename IteratorType::container_type&>(*this));
return index_type::make_iterator( return index_type::make_iterator(
static_cast<final_node_type*>(it.get_node())); static_cast<final_node_type*>(it.get_node()));
} }
@ -767,6 +763,19 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
} }
} }
template<typename Index>
std::pair<final_node_type*,bool> transfer_(Index& x,final_node_type* n)
{
final_node_type* res=super::insert_(n->value(),n,&super::final(x));
if(res==n){
++node_count;
return std::pair<final_node_type*,bool>(res,true);
}
else{
return std::pair<final_node_type*,bool>(res,false);
}
}
template<BOOST_MULTI_INDEX_TEMPLATE_PARAM_PACK> template<BOOST_MULTI_INDEX_TEMPLATE_PARAM_PACK>
std::pair<final_node_type*,bool> emplace_( std::pair<final_node_type*,bool> emplace_(
BOOST_MULTI_INDEX_FUNCTION_PARAM_PACK) BOOST_MULTI_INDEX_FUNCTION_PARAM_PACK)
@ -923,14 +932,21 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
final_node_handle_type extract_(final_node_type* x) final_node_handle_type extract_(final_node_type* x)
{ {
--node_count; --node_count;
super::extract_(x); super::extract_(x,detail::invalidate_iterators());
return final_node_handle_type(x,get_allocator()); return final_node_handle_type(x,get_allocator());
} }
template<typename Dst>
void extract_for_transfer_(final_node_type* x,Dst dst)
{
--node_count;
super::extract_(x,dst);
}
void erase_(final_node_type* x) void erase_(final_node_type* x)
{ {
--node_count; --node_count;
super::extract_(x); super::extract_(x,detail::invalidate_iterators());
delete_node_(x); delete_node_(x);
} }
@ -952,6 +968,17 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
node_count=0; node_count=0;
} }
template<typename Index>
void transfer_range_(
Index& x,
BOOST_DEDUCED_TYPENAME Index::iterator first,
BOOST_DEDUCED_TYPENAME Index::iterator last)
{
while(first!=last){
transfer_(x,static_cast<final_node_type*>((first++).get_node()));
}
}
void swap_(multi_index_container<Value,IndexSpecifierList,Allocator>& x) void swap_(multi_index_container<Value,IndexSpecifierList,Allocator>& x)
{ {
swap_( swap_(
@ -1332,14 +1359,7 @@ project(
#endif #endif
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it);
BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,m);
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
typedef detail::converter<
multi_index_type,
BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter;
BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m));
#endif
return detail::converter<multi_index_type,index_type>::iterator( return detail::converter<multi_index_type,index_type>::iterator(
m,static_cast<typename multi_index_type::final_node_type*>(it.get_node())); m,static_cast<typename multi_index_type::final_node_type*>(it.get_node()));
} }
@ -1368,14 +1388,7 @@ project(
#endif #endif
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it);
BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,m);
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
typedef detail::converter<
multi_index_type,
BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter;
BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m));
#endif
return detail::converter<multi_index_type,index_type>::const_iterator( return detail::converter<multi_index_type,index_type>::const_iterator(
m,static_cast<typename multi_index_type::final_node_type*>(it.get_node())); m,static_cast<typename multi_index_type::final_node_type*>(it.get_node()));
} }
@ -1418,14 +1431,7 @@ project(
#endif #endif
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it);
BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,m);
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
typedef detail::converter<
multi_index_type,
BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter;
BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m));
#endif
return detail::converter<multi_index_type,index_type>::iterator( return detail::converter<multi_index_type,index_type>::iterator(
m,static_cast<typename multi_index_type::final_node_type*>(it.get_node())); m,static_cast<typename multi_index_type::final_node_type*>(it.get_node()));
} }
@ -1455,14 +1461,7 @@ project(
#endif #endif
BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it);
BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,m);
#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
typedef detail::converter<
multi_index_type,
BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter;
BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m));
#endif
return detail::converter<multi_index_type,index_type>::const_iterator( return detail::converter<multi_index_type,index_type>::const_iterator(
m,static_cast<typename multi_index_type::final_node_type*>(it.get_node())); m,static_cast<typename multi_index_type::final_node_type*>(it.get_node()));
} }

View File

@ -1,6 +1,6 @@
/* Boost.MultiIndex test for standard list operations. /* Boost.MultiIndex test for standard list operations.
* *
* Copyright 2003-2013 Joaquin M Lopez Munoz. * Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -76,6 +76,7 @@ template<typename Sequence>
static void test_list_ops_unique_seq() static void test_list_ops_unique_seq()
{ {
typedef typename nth_index<Sequence,1>::type sequenced_index; typedef typename nth_index<Sequence,1>::type sequenced_index;
typedef typename sequenced_index::iterator sequenced_index_iterator;
Sequence ss,ss2; Sequence ss,ss2;
sequenced_index &si=get<1>(ss),&si2=get<1>(ss2); sequenced_index &si=get<1>(ss),&si2=get<1>(ss2);
@ -99,20 +100,32 @@ static void test_list_ops_unique_seq()
CHECK_EQUAL(si,(3)(5)(1)); CHECK_EQUAL(si,(3)(5)(1));
si.splice(si.end(),si2); si.splice(si.end(),si2,project<1>(ss2,ss2.find(2)),si2.end());
CHECK_EQUAL(si,(3)(5)(1)(2)(6));
CHECK_EQUAL(si2,(3)(4)(0)(8)(5)(1));
si.splice(project<1>(ss,ss.find(2)),si2);
CHECK_EQUAL(si,(3)(5)(1)(4)(0)(8)(2)(6)); CHECK_EQUAL(si,(3)(5)(1)(4)(0)(8)(2)(6));
CHECK_EQUAL(si2,(3)(5)(1)); CHECK_EQUAL(si2,(3)(5)(1));
si.splice(project<1>(ss,ss.find(4)),si,project<1>(ss,ss.find(8))); si.splice(project<1>(ss,ss.find(4)),si,project<1>(ss,ss.find(8)));
CHECK_EQUAL(si,(3)(5)(1)(8)(4)(0)(2)(6)); CHECK_EQUAL(si,(3)(5)(1)(8)(4)(0)(2)(6));
si2.clear();
si2.splice(si2.begin(),si,si.begin());
si.splice(si.end(),si2,si2.begin()); si2.clear();
std::pair<sequenced_index_iterator,bool> p=
si2.splice(si2.begin(),si,si.begin());
BOOST_TEST(*(p.first)==3&&p.second);
p=si.splice(si.end(),si2,si2.begin());
BOOST_TEST(*(p.first)==3&&p.second);
CHECK_EQUAL(si,(5)(1)(8)(4)(0)(2)(6)(3)); CHECK_EQUAL(si,(5)(1)(8)(4)(0)(2)(6)(3));
BOOST_TEST(si2.empty()); BOOST_TEST(si2.empty());
si2.splice(si2.end(),si,project<1>(ss,ss.find(0)),project<1>(ss,ss.find(6))); si2.splice(si2.end(),si,project<1>(ss,ss.find(2)),project<1>(ss,ss.find(6)));
CHECK_EQUAL(si,(5)(1)(8)(4)(0)(6)(3));
CHECK_EQUAL(si2,(2));
si2.splice(si2.begin(),si,project<1>(ss,ss.find(0)),project<1>(ss,ss.find(6)));
CHECK_EQUAL(si,(5)(1)(8)(4)(6)(3)); CHECK_EQUAL(si,(5)(1)(8)(4)(6)(3));
CHECK_EQUAL(si2,(0)(2)); CHECK_EQUAL(si2,(0)(2));

View File

@ -1,6 +1,6 @@
/* Boost.MultiIndex test for node handling operations. /* Boost.MultiIndex test for node handling operations.
* *
* Copyright 2003-2020 Joaquin M Lopez Munoz. * Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -23,8 +23,12 @@
#include <boost/multi_index/ranked_index.hpp> #include <boost/multi_index/ranked_index.hpp>
#include <boost/multi_index/sequenced_index.hpp> #include <boost/multi_index/sequenced_index.hpp>
#include <boost/next_prior.hpp> #include <boost/next_prior.hpp>
#include <boost/type_traits/integral_constant.hpp> #include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <functional> #include <functional>
#include <iterator>
#include <utility>
#include <vector>
#include "count_allocator.hpp" #include "count_allocator.hpp"
using namespace boost::multi_index; using namespace boost::multi_index;
@ -136,7 +140,8 @@ template<typename Index>
struct is_key_based:boost::integral_constant< struct is_key_based:boost::integral_constant<
bool, bool,
/* rather fragile if new index types are included in the library */ /* rather fragile if new index types are included in the library */
(boost::tuples::length<typename Index::ctor_args>::value > 0) (boost::tuples::length<typename boost::remove_reference<Index>::
type::ctor_args>::value > 0)
> >
{}; {};
@ -403,8 +408,410 @@ void test_transfer()
BOOST_TEST(src.size()==8); BOOST_TEST(src.size()==8);
} }
template<typename T>
struct enable_if_key_based:boost::enable_if_c<
is_key_based<T>::value,
void*
>{};
template<typename T>
struct enable_if_not_key_based:boost::enable_if_c<
!is_key_based<T>::value,
void*
>{};
/* Boost.Move C++03 perfect forwarding emulation converts non-const lvalue
* refs to const lvalue refs. final_forward undoes that.
*/
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template<typename T>
T&& final_forward(typename boost::remove_reference<T>::type& x)
{
return static_cast<T&&>(x);
}
#else
template<typename T>
T& final_forward(const T& x){return const_cast<T&>(x);}
#endif
template<typename Dst,typename Src>
void merge(
Dst& dst,BOOST_FWD_REF(Src) src,
typename enable_if_key_based<Dst>::type=0)
{
dst.merge(final_forward<Src>(src));
}
template<typename Dst,typename Src>
void merge(
Dst& dst,BOOST_FWD_REF(Src) src,
typename enable_if_not_key_based<Dst>::type=0)
{
dst.splice(dst.end(),final_forward<Src>(src));
}
template<typename Dst,typename Src>
std::pair<typename Dst::iterator,bool> merge(
Dst& dst,BOOST_FWD_REF(Src) src,
typename boost::remove_reference<Src>::type::iterator i,
typename enable_if_key_based<Dst>::type=0)
{
return dst.merge(final_forward<Src>(src),i);
}
template<typename Dst,typename Src>
std::pair<typename Dst::iterator,bool> merge(
Dst& dst,BOOST_FWD_REF(Src) src,
typename boost::remove_reference<Src>::type::iterator i,
typename enable_if_not_key_based<Dst>::type=0)
{
return dst.splice(dst.end(),final_forward<Src>(src),i);
}
template<typename Dst,typename Src>
void merge(
Dst& dst,BOOST_FWD_REF(Src) src,
typename boost::remove_reference<Src>::type::iterator first,
typename boost::remove_reference<Src>::type::iterator last,
typename enable_if_key_based<Dst>::type=0)
{
dst.merge(final_forward<Src>(src),first,last);
}
template<typename Dst,typename Src>
void merge(
Dst& dst,BOOST_FWD_REF(Src) src,
typename boost::remove_reference<Src>::type::iterator first,
typename boost::remove_reference<Src>::type::iterator last,
typename enable_if_not_key_based<Dst>::type=0)
{
dst.splice(dst.end(),final_forward<Src>(src),first,last);
}
template<typename Dst,typename Src>
void test_merge_same(Dst& dst,BOOST_FWD_REF(Src) src)
{
std::size_t n=dst.size();
merge(dst,boost::forward<Src>(src));
BOOST_TEST(dst.size()==n);
BOOST_TEST(src.size()==n);
}
template<typename Iterator,typename Value>
bool find_address(Iterator first,Iterator last,const Value* x)
{
while(first!=last)if(&*first++==x)return true;
return false;
}
template<typename Dst,typename Src>
void test_merge_different(
Dst& dst,BOOST_FWD_REF(Src) src,bool transferred_iters)
{
typedef typename boost::remove_reference<Src>::
type::iterator src_iterator;
typedef typename boost::remove_reference<Src>::
type::value_type src_value_type;
std::size_t n=dst.size(),m=src.size();
std::vector<
std::pair<src_iterator,const src_value_type*>
> v;
for(src_iterator first=src.begin(),last=src.end();first!=last;++first){
v.push_back(std::make_pair(first,&*first));
}
merge(dst,boost::forward<Src>(src));
BOOST_TEST(dst.size()>=n && m>=src.size() && dst.size()-n==m-src.size());
for(std::size_t i=0;i<v.size();++i){
BOOST_TEST(
find_address(src.begin(),src.end(),v[i].second)||
find_address(dst.begin(),dst.end(),v[i].second));
}
if(transferred_iters){
for(std::size_t i=0;i<v.size();++i){
BOOST_TEST(&*(v[i].first)==v[i].second);
}
}
}
template<typename Dst,typename Src>
void test_merge_same(
Dst& dst,BOOST_FWD_REF(Src) src,
typename boost::remove_reference<Src>::type::iterator i,
bool key_based=is_key_based<Dst>::value)
{
typedef typename Dst::iterator dst_iterator;
std::size_t n=dst.size();
std::pair<dst_iterator,bool> p=merge(dst,boost::forward<Src>(src),i);
BOOST_TEST(dst.size()==n);
BOOST_TEST(src.size()==n);
BOOST_TEST(&*(p.first)==&*i && p.second);
if(!key_based)BOOST_TEST(boost::next(p.first)==dst.end());
}
template<typename Dst,typename Src>
void test_merge_different(
Dst& dst,BOOST_FWD_REF(Src) src,
typename boost::remove_reference<Src>::type::iterator i,
bool key_based=is_key_based<Dst>::value)
{
typedef typename Dst::iterator dst_iterator;
std::size_t n=dst.size(),m=src.size();
const typename Dst::value_type* pi=&*i;
std::pair<dst_iterator,bool> p=merge(dst,boost::forward<Src>(src),i);
BOOST_TEST(dst.size()+src.size()==n+m);
BOOST_TEST(p.second?
&*(p.first)==pi:
&*(p.first)!=pi && *(p.first)==*i);
if(!key_based)BOOST_TEST(!p.second || boost::next(p.first)==dst.end());
}
template<typename Dst,typename Src>
void test_merge_same(
Dst& dst,BOOST_FWD_REF(Src) src,
typename boost::remove_reference<Src>::type::iterator first,
typename boost::remove_reference<Src>::type::iterator last,
bool key_based=is_key_based<Dst>::value)
{
typedef typename Dst::iterator dst_iterator;
typedef typename boost::remove_reference<Src>::
type::iterator src_iterator;
typedef typename boost::remove_reference<Src>::
type::value_type src_value_type;
std::size_t n=dst.size(),d=std::distance(first,last);
std::vector<const src_value_type*> v;
for(src_iterator it=first;it!=last;++it)v.push_back(&*it);
merge(dst,boost::forward<Src>(src),first,last);
BOOST_TEST(dst.size()==n);
BOOST_TEST(src.size()==n);
if(!key_based){
dst_iterator it=boost::next(dst.begin(),dst.size()-d);
for(std::size_t i=0;i<d;++i){
BOOST_TEST(&*it++==v[i]);
}
}
else{
src_iterator it=first;
for(std::size_t i=0;i<d;++i){
BOOST_TEST(&*it++==v[i]);
}
}
}
template<typename Dst,typename Src>
void test_merge_different(
Dst& dst,BOOST_FWD_REF(Src) src,
typename boost::remove_reference<Src>::type::iterator first,
typename boost::remove_reference<Src>::type::iterator last,
bool key_based=is_key_based<Dst>::value)
{
typedef typename Dst::iterator dst_iterator;
typedef typename boost::remove_reference<Src>::
type::iterator src_iterator;
typedef typename boost::remove_reference<Src>::
type::value_type src_value_type;
std::size_t n=dst.size(),
m=src.size();
std::vector<const src_value_type*> v;
for(src_iterator it=first;it!=last;++it)v.push_back(&*it);
merge(dst,boost::forward<Src>(src),first,last);
BOOST_TEST(dst.size()>=n && m>=src.size() && dst.size()-n==m-src.size());
if(!key_based){
for(dst_iterator it=boost::next(dst.begin(),n);it!=dst.end();++it){
BOOST_TEST(std::find(v.begin(),v.end(),&*it)!=v.end());
}
}
for(std::size_t i=0;i<v.size();++i){
BOOST_TEST(
find_address(src.begin(),src.end(),v[i])||
find_address(dst.begin(),dst.end(),v[i]));
}
}
template<int N,int M,typename Dst>
void test_merge_same(Dst& dst)
{
const Dst dst1=dst;
{
Dst dst2=dst1;
test_merge_same(
dst2.template get<N>(),dst2.template get<M>());
test_merge_same(
dst2.template get<N>(),boost::move(dst2.template get<M>()));
}
{
Dst dst2=dst1;
test_merge_same(
dst2.template get<N>(),dst2.template get<M>(),
boost::next(dst2.template get<M>().begin(),dst2.size()/2));
test_merge_same(
dst2.template get<N>(),boost::move(dst2.template get<M>()),
boost::next(dst2.template get<M>().begin(),dst2.size()/2));
}
{
Dst dst2=dst1;
test_merge_same(
dst2.template get<N>(),dst2.template get<M>(),
dst2.template get<M>().begin(),
boost::next(dst2.template get<M>().begin(),dst2.size()/2));
test_merge_same(
dst2.template get<N>(),boost::move(dst2.template get<M>()),
dst2.template get<M>().begin(),
boost::next(dst2.template get<M>().begin(),dst2.size()/2));
}
}
template<int N,int M,typename Dst,typename Src>
void test_merge_different(Dst& dst,Src& src)
{
const Dst dst1=dst;
const Src src1=src;
const bool transferred_iters=
boost::is_same<
typename boost::multi_index::nth_index<Dst,M>::type::iterator,
typename boost::multi_index::nth_index<Src,M>::type::iterator>::value;
{
Dst dst2=dst1;
Src src2=src1;
test_merge_different(
dst2.template get<N>(),src2.template get<M>(),transferred_iters);
}
{
Dst dst2=dst1;
Src src2=src1;
test_merge_different(
dst2.template get<N>(),boost::move(src2.template get<M>()),
transferred_iters);
}
{
Dst dst2=dst1;
Src src2=src1;
test_merge_different(
dst2.template get<N>(),src2.template get<M>(),
boost::next(src2.template get<M>().begin(),src2.size()/2));
}
{
Dst dst2=dst1;
Src src2=src1;
test_merge_different(
dst2.template get<N>(),boost::move(src2.template get<M>()),
boost::next(src2.template get<M>().begin(),src2.size()/2));
}
{
Dst dst2=dst1;
Src src2=src1;
test_merge_different(
dst2.template get<N>(),src2.template get<M>(),
src2.template get<M>().begin(),
boost::next(src2.template get<M>().begin(),src2.size()/2));
}
{
Dst dst2=dst1;
Src src2=src1;
test_merge_different(
dst2.template get<N>(),boost::move(src2.template get<M>()),
src2.template get<M>().begin(),
boost::next(src2.template get<M>().begin(),src2.size()/2));
}
}
template<int N,int M,typename Dst,typename Src>
void test_merge(Dst& dst,Src& src)
{
test_merge_different<N,M>(dst,src);
}
template<int N,int M,typename Dst>
void test_merge(Dst& dst,Dst& src)
{
if(&dst==&src)test_merge_same<N,M>(dst);
else test_merge_different<N,M>(dst,src);
}
struct another_int_hash
{
std::size_t operator()(int x)const
{
return boost::hash<int>()(x)*2;
}
};
void test_merge()
{
typedef multi_index_container<
int,
indexed_by<
ordered_non_unique<identity<int> >,
hashed_non_unique<identity<int> >,
random_access<>,
sequenced<>,
ranked_non_unique<identity<int> >
>
> container1;
typedef multi_index_container<
int,
indexed_by<
ordered_unique<
identity<int>,
std::greater<int>
>,
hashed_unique<
identity<int>,
another_int_hash
>,
random_access<>,
sequenced<>,
ranked_unique<
identity<int>,
std::greater<int>
>
>
> container2;
container1 c1;
container2 c2;
for(int i=0;i<10;++i){
c1.insert(i);
c2.insert(2*i);
}
test_merge<0,1>(c1,c1);
test_merge<1,2>(c1,c1);
test_merge<2,3>(c1,c1);
test_merge<3,4>(c1,c1);
test_merge<4,0>(c1,c1);
test_merge<0,3>(c2,c2);
test_merge<1,4>(c2,c2);
test_merge<4,2>(c2,c2);
test_merge<0,1>(c1,c2);
test_merge<1,2>(c1,c2);
test_merge<2,3>(c1,c2);
test_merge<3,4>(c1,c2);
test_merge<4,0>(c1,c2);
test_merge<0,3>(c2,c1);
test_merge<1,4>(c2,c1);
test_merge<4,2>(c2,c1);
}
void test_node_handling() void test_node_handling()
{ {
test_node_handle(); test_node_handle();
test_transfer(); test_transfer();
test_merge();
} }

View File

@ -1,6 +1,6 @@
/* Boost.MultiIndex test for safe_mode. /* Boost.MultiIndex test for safe_mode.
* *
* Copyright 2003-2020 Joaquin M Lopez Munoz. * Copyright 2003-2021 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -326,7 +326,7 @@ static void local_test_safe_mode_with_rearrange()
TRY_SAFE_MODE TRY_SAFE_MODE
i.splice(i.begin(),i); i.splice(i.begin(),i);
CATCH_SAFE_MODE(safe_mode::same_container) CATCH_SAFE_MODE(safe_mode::inside_range)
TRY_SAFE_MODE TRY_SAFE_MODE
iterator it; iterator it;