<html>
<head>
<title>Comments on LWG issue 233: Insertion hints in associative containers</title>
<style>
p {text-align:justify}
li {text-align:justify}
blockquote.note {background-color:silver}
ins {color:red}
</style>
</head>

<body>

<address align=right>
Document number: N1780=05-0040<br>
<br>
<a href="mailto:hinnant@twcny.rr.com">Howard E. Hinnant</a><br>
2005-04-23
</address>
<hr>

<h1 align=center>Comments on LWG issue 233:<br>Insertion hints in associative containers</h1>

<h2>Contents</h2>

<ul>
<li><a href="#Introduction">Introduction</a></li>
<li><a href="#Current Practice">Current Practice</a></li>
<li>
<a href="#Proposal">Proposal</a>
<ul>
<li><a href="#Before, not after">Before, not after</a></li>
<li><a href="#As close as possible to hint">As close as possible to hint</a></li>
<li><a href="#Equal range stability">Equal range stability</a></li>
<li><a href="#Insert without hint">Insert without hint</a></li>
<li><a href="#Efficiency Concerns">Efficiency Concerns</a></li>
<li><a href="#Prior Art">Prior Art</a></li>
<li><a href="#Summary">Summary</a></li>
<li><a href="#Proposed Wording">Proposed Wording</a></li>
</ul>
</li>
<li><a href="#Acknowledments">Acknowledments</a></li>
</ul>

<h2><a name="Introduction"></a>Introduction</h2>

<p>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#233">LWG
issue 233</a>
concerns the semantics of the "hint" in the insert member of associative member
containers.  The current standard reads:
</p>

<p><center>
<table border="1">
<caption>Associative container requirements</caption>
<tr><th>expression</th> <th>return type</th>
<th>assertion/note<br>pre/post-condition</th>
<th>complexity</th></tr>
<tr><td><tt>a.insert(p,t)</tt></td>
<td><tt>iterator</tt></td>
<td>
inserts <tt>t</tt> if and only if there is no element with key equivalent to the
key of <tt>t</tt> in containers with unique keys; always inserts <tt>t</tt> in containers
with equivalent keys. always returns the iterator pointing to the element with key
equivalent to the key of <tt>t</tt>. iterator <tt>p</tt> is a hint pointing to where
the insert should start to search.
</td>
<td>
logarithmic in general, but amortized constant if <tt>t</tt> is inserted right after <tt>p</tt>.
</td></tr>
</table>
</center></p>

<p>
There are two things wrong with this requirement.
</p>

<ol>
<li>
The complexity column implies that insertion of <tt>t</tt> <i>right after</i>
<tt>p</tt> is the preferred location, whereas sequence containers have the
semantics of insert <tt>t</tt> <i>before</i> <tt>p</tt>.
</li>
<li>
<tt>p</tt> is referred to as a hint only (for performance purposes), rather than
a preferred insertion location in a multi(map/set).
</li>
</ol>

<p>
The first imperfection is arguably a simple "think-o" in the original standard.
 It should have stated <i>before</i> instead of <i>after</i>.  How else is one
to suggest that an element should be inserted before the first element?  And
with <i>after</i> semantics, what does after <tt>end()</tt> mean?  Whereas with
<i>before</i> semantics, every valid iterator used as a hint into a container
has a well defined meaning, and any location can be specified.
</p>

<p>
The second problem reflects a lost opportunity.  Especially in the case of
<tt>multimap</tt>, but also even in <tt>multiset</tt>, just because two keys are
equivalent, it doesn't mean that there is no extra information in the
<tt>value_type</tt>.  And the order in which those elements appear within the
<tt>equal_range</tt> may impart vital information to the client.  For example it
might be that elements later in an <tt>equal_range</tt> represent elements that
were inserted at a later time than prior elements in the <tt>equal_range</tt>.
And that information may be important to the client. There is no need for the
client to also have to store a time stamp for this purpose if he can simply
control the ordering of elements within an <tt>equal_range</tt>.
</p>

<p>
The <a
href="http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#233">current
proposed resolution</a> was last actively discussed in the late 2001 /
early 2002 time frame.  This is significant because at that time, the LWG was
only fixing defects, and specifically not entertaining design improvements. It
is now 2005 and the committee <i>is</i> entertaining improvements for C++0X. And
one of the big improvements that can be made in this area is to give the clients
of associative containers control over the ordering of elements within an
<tt>equal_range</tt>.
</p>

<p>
For example, what should the following code output?
</p>

<blockquote><pre>
#include &lt;map&gt;
#include &lt;string&gt;
#include &lt;utility&gt;
#include &lt;iostream&gt;

template &lt;class Map&gt;
void
display(const Map& m)
{
    for (typename Map::const_iterator i = m.begin(), e = m.end(); i != e; ++i)
        std::cout &lt;&lt; '(' &lt;&lt; i-&gt;first &lt;&lt; ", " &lt;&lt; i-&gt;second &lt;&lt; ") ";
    std::cout &lt;&lt; '\n';
}

int main()
{
    typedef std::multimap&lt;int, std::string&gt; Map;
    typedef std::pair&lt;int, std::string&gt; value_type;
    Map m;
    m.insert(value_type(0, "zero"));
    m.insert(value_type(1, "one"));
    m.insert(value_type(2, "two"));
    display(m);
    m.insert(m.find(1), value_type(1, "ONE"));
    display(m);
}
</pre></blockquote>

<p>
The current standard isn't really clear, but implies:
</p>

<blockquote><pre>
(0, zero) (1, one) (2, two)
(0, zero) (1, one) (1, ONE) (2, two)
</pre></blockquote>

<p>
That is, the new element is inserted after the current "equal element".  With
such an implementation, there is no way to store a <tt>(0, ZERO)</tt> such that
the new element becomes the first element in the sequence:
</p>

<blockquote><pre>
(0, zero) (1, one) (2, two)
(0, ZERO) (0, zero) (1, one) (2, two)
</pre></blockquote>

<p>
The <a
href="http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#233">current
proposed resolution</a> does not solve this problem.  It simply swaps
the word <i>adjacent</i> for <i>after</i> to clarify that an implementation is
allowed (but not required) to first check prior to the hint.  This gives library
vendors leeway, but does not give the client control of <tt>equal_range</tt>
ordering.
</p>

<h2><a name="Current Practice"></a>Current Practice</h2>

<p>
A brief survey of current library implementations reveals differing behavior.
Though as the text in
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#233">LWG
issue 233</a>
argues, all implementations can be seen as conforming on this point.
</p>

<p>
The following libraries
</p>
<blockquote><p>
Dinkumware<br> gcc prior to version 4<br> Metrowerks CodeWarrior<br>STLport
</p></blockquote>
<p>
output:
</p>

<blockquote><pre>
(0, zero) (1, one) (2, two)
(0, zero) (1, ONE) (1, one) (2, two)
</pre></blockquote>

<p>
Rogue Wave, and possibly gcc 4.0 output:
</p>

<blockquote><pre>
(0, zero) (1, one) (2, two)
(0, zero) (1, one) (1, ONE) (2, two)
</pre></blockquote>

<p>
That is, today control over <tt>equal_range</tt> ordering, if it can be obtained
at all, is not portable (even across different versions of the same library).
And the current (<i>adjacent</i>) resolution to
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#233">LWG
issue 233</a>
is set to cement that fact.
</p>

<h2><a name="Proposal"></a>Proposal</h2>

<h3><a name="Before, not after"></a>Before, not after</h3>

<p>
This paper proposes to make the output of the code example above deterministic
by specifying that <i>before</i> semantics should be used instead of
<i>adjacent</i> or <i>after</i> semantics, both for consistency with sequence
containers, and so that insert before or after any element in the sequence can
be specified (including before <tt>begin()</tt>).
</p>

<p>
If this proposal is accepted then the above program will portably display:
</p>

<blockquote><pre>
(0, zero) (1, one) (2, two)
(0, zero) (1, ONE) (1, one) (2, two)
</pre></blockquote>

<p>
This is new functionality, not available in C++03 (at least not portably).  This
paper submits that this new functionality is valuable, and costs virtually
nothing in terms of code size, computational expense, or vendor effort.  Clients
of <tt>std::multiset</tt> and <tt>std::multimap</tt> will be able to view
<tt>equal_range</tt>'s as sub-sequences within the container.  And will be able
to control the order within those sub-sequences throughout the lifetime of the
container.
</p>

<p>
Note that specifying <i>before</i> semantics does not prohibit an implementation
from checking <i>after</i> the hint.  It only means that implementations must
prefer the <i>before</i> location.  A subsequent <i>after</i> check could
optionally be implemented at the vendor's discretion.
</p>

<p>
The specification of <i>before</i> semantics is a giant step forward but only
gives the client partial control over the ordering within an
<tt>equal_range</tt>.  If the hint turns out to be wrong, the client still
looses control over where within an <tt>equal_range</tt> the new element will be
inserted.
</p>

<h3><a name="As close as possible to hint"></a>As close as possible to hint</h3>

<p>
Andrew Koenig proposed in 2000 the "as close as possible to hint" rule:
</p>

<blockquote><p>
The proposed resolution was that the new element should always be inserted as
close to the hint as possible. So, for example, if there is a subsequence of
equivalent values, then providing a.begin() as the hint means that the new
element should be inserted before the subsequence even if a.begin() is far away.
</p></blockquote>

<p>
This allows code to always append (or prepend) an equal range with something as
simple as:
</p>

<blockquote><pre>
m.insert(m.end(), value_type(an_int, the_string));
</pre></blockquote>

<p>or</p>

<blockquote><pre>
m.insert(m.begin(), value_type(an_int, the_string));
</pre></blockquote>

<p>
Without this suggestion, the semantics of append (or prepend) to an equal range
is somewhat more cumbersome to code:
</p>

<blockquote><pre>
int an_int = key;
m.insert(m.upper_bound(an_int), value_type(an_int, the_string));
</pre></blockquote>

<p>
If inserting a nearly sorted range into a <tt>multimap/set</tt> (and for
whatever reasons you cannot use the iterator-range insert member), then Andrew's
suggestion becomes more than just a more convenient interface; <b>it becomes
computationally more efficient</b> while ensuring that equal ranges within the
source remain stable as they are inserted into the <tt>multiset</tt> or
<tt>multimap</tt> target.<br>For example:
</p>

<blockquote><pre>
while (inserting)
{
    ...
    int key = obtain_int();
    ...
    string value = obtain_string();
    <b>m.insert(m.end(), value_type(key, value));</b>
}
</pre></blockquote>

<p>vs.</p>

<blockquote><pre>
while (inserting)
{
    ...
    int key = obtain_int();
    ...
    string value = obtain_string();
    <b>m.insert(m.upper_bound(key), value_type(key, value));</b>
}
</pre></blockquote>

<p>
When the key's are encountered in nearly sorted order (and starting with an
empty container), then the former loop has O(N) complexity while the latter loop
has O(N*log(N)) complexity.  If one executed the former loop in order to
maintain efficiency, but without the "as close to the hint as possible" rule
implemented, then for those few cases where <tt>end()</tt> turned out to be a
bad suggestion, the stable order of equal ranges is no longer guaranteed.  To
reestablish that guarantee, you must code the more expensive loop with a call to
<tt>upper_bound</tt> for each iteration. Or alternatively one could manually
code one of the algorithms presented below (essentially passing responsibility
for efficient code from the library vendor to the client).
</p>

<h3><a name="Equal range stability"></a>Equal range stability</h3>

<p>
Once the client is able to fully specify where within an <tt>equal_range</tt> a
new element is inserted, there must be some guarantee that the resulting
<tt>equal_range</tt> is stable throughout the lifetime of the container.
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#371">LWG
issue 371</a>
addresses this concern and provides additional motivation for requiring such
stability:
</p>

<p>
As client code iterates through a <tt>multimap</tt> or <tt>multiset</tt> and
selectively erases elements, if the ordering of the elements (even within an
<tt>equal_range</tt>) is not stable during this process, then the client could
unknowingly avoid iterating over some of the elements in a container.  This
would break well established idioms:
</p>

<blockquote><pre>
multimap&lt;int, int&gt; m;
...
multimap&lt;int, int&gt;::iterator i = m.begin();
while (i != m.end())
{
    if (pred(i))
        m.erase (i++);
    else
        ++i;
}
</pre></blockquote>

<p>
Fortunately binary tree algorithms are designed to preserve in-order order. Node
<i>rotations</i> used to rebalance trees (be they AVL, red-black or whatever) do
not reorder elements, even if they are equivalent.  An implementation would have
to go to extra expense, both in terms of code size and in terms of run time
computation, in order to change the relative order of elements within an
<tt>equal_range</tt>. Therefore the current status-quo is that all current
implementations have the characteristic that <tt>equal_range</tt>'s are stable.
</p>

<h3><a name="Insert without hint"></a>Insert without hint</h3>

<p>
During the course of writing this paper several people expressed to me the
desire to make the "insert without hint" function deterministic with respect to
<tt>equal_range</tt>'s. My first impression was "not gonna' happen."  Not
because it was too expensive, or too difficult, but because it would be too
controversial in that it would overly restrict vendor options in implementation.
</p>

<p>
However after further reflection I have changed my mind.
</p>

<p>
The well-referenced book "Introduction To Algorithms" by Thomas H. Cormen,
Charles E. Leiserson and Ronald L. Rivest describes binary tree options in
detail, including the red-black and AVL balancing algorithms.  The general
(non-balanced) algorithm for inserting into a binary tree is given as:
</p>

<blockquote><pre>
Tree-Insert(T, z)
   y &lt;- NIL
   x &lt;- root[T]
   while x != NIL
      do y &lt;- x
         if key[z] &lt; key[x]
            then x &lt;- left[x]
            else x &lt;- right[x]
   p[z] &lt;- y
   if y == NIL
      then root[T] &lt;- z
      else if key[z] &lt; key[y]
         then left[y] &lt;- z
         else right[y] &lt;- z
</pre></blockquote>

<p>
And the balanced-tree algorithms (whether red-black or AVL) start with this
unbalanced insert and then proceed to fix up invariants in the tree structure
which are broken by the insert. (Disclaimer:  red-black is discussed in much
more detail than AVL, but I believe the assumption above is valid).
</p>

<p>
There are two interesting things to notice about this algorithm:
</p>

<ol>
<li>
When inserting a new <tt>value_type</tt>, the algorithm always searches to a
leaf node (to the bottom of the tree) to insert a node.  Nodes are never spliced
in mid-way down a tree.
</li>
<li>
If the tree contains an existing <tt>equal_range</tt> of <tt>z</tt>, this
algorithm always appends to the <tt>upper_bound</tt> of it.
</li>
</ol>

<p>
The first point highlights that to find the point of insertion one must traverse
the full height of the tree.   Finding an equivalent element mid way down, or
even at the root, does not preclude the need to navigate down to the leaves of
the tree.  So even if an entire tree consists on one very long
<tt>equal_range</tt>, if you're going to insert anywhere into that tree, you
must still navigate from the root to the leaves, no matter what your
<tt>equal_range</tt> insertion strategy.
</p>

<p>
The second point highlights that "insert without hint" and "insert at upper
bound" can be the exact same operation, with the exact same cost. Indeed,
inspection of the original HP implementation reveals the basic
Coren/Leiserson/Rivest algorithm.  Glancing at the May 1997 issue of CUJ,
article "Implementing Associative Containers" by P. J. Plauger reveals the same
heritage.  A glance at a recent gcc implementation again confirms this trend.
Finally a recent survey of current practice confirms that all current
implementations of <tt>multi_map</tt> always insert at the upper bound of an
equal range when using the "insert without hint" member function.
</p>

<blockquote><pre>
#include &lt;map&gt;
#include &lt;string&gt;
#include &lt;iostream&gt;
#include &lt;utility&gt;

template &lt;class Map&gt;
void
display(const Map& m)
{
    for (typename Map::const_iterator i = m.begin(), e = m.end(); i != e; ++i)
        std::cout &lt;&lt; '(' &lt;&lt; i-&gt;first &lt;&lt; ", " &lt;&lt; i-&gt;second &lt;&lt; ") ";
    std::cout &lt;&lt; '\n';
}

int main()
{
    typedef std::multimap&lt;int, std::string&gt; Map;
    typedef std::pair&lt;int, std::string&gt; value_type;
    Map m;
    m.insert(value_type(3, "three"));
    m.insert(value_type(3, "threE"));
    m.insert(value_type(3, "thrEe"));
    m.insert(value_type(3, "thrEE"));
    m.insert(value_type(3, "thRee"));
    m.insert(value_type(3, "thReE"));
    m.insert(value_type(3, "thREe"));
    m.insert(value_type(3, "thREE"));
    m.insert(value_type(3, "tHree"));
    m.insert(value_type(3, "tHreE"));
    m.insert(value_type(3, "tHrEe"));
    m.insert(value_type(3, "tHrEE"));
    m.insert(value_type(3, "tHRee"));
    m.insert(value_type(3, "tHReE"));
    m.insert(value_type(3, "tHREe"));
    m.insert(value_type(3, "tHREE"));
    m.insert(value_type(3, "Three"));
    m.insert(value_type(3, "ThreE"));
    m.insert(value_type(3, "ThrEe"));
    m.insert(value_type(3, "ThrEE"));
    m.insert(value_type(3, "ThRee"));
    m.insert(value_type(3, "ThReE"));
    m.insert(value_type(3, "ThREe"));
    m.insert(value_type(3, "ThREE"));
    m.insert(value_type(3, "THree"));
    m.insert(value_type(3, "THreE"));
    m.insert(value_type(3, "THrEe"));
    m.insert(value_type(3, "THrEE"));
    m.insert(value_type(3, "THRee"));
    m.insert(value_type(3, "THReE"));
    m.insert(value_type(3, "THREe"));
    m.insert(value_type(3, "THREE"));
    display(m);
}
</pre></blockquote>

<p>
The following implementations:
</p>
<blockquote><p>
Dinkumware<br> gcc<br> Metrowerks CodeWarrior<br>Rogue Wave<br>STLport
</p></blockquote>
<p>
output:
</p>
<blockquote><pre>
(3, three) (3, threE) (3, thrEe) (3, thrEE) (3, thRee) (3, thReE) (3, thREe) (3, thREE)
(3, tHree) (3, tHreE) (3, tHrEe) (3, tHrEE) (3, tHRee) (3, tHReE) (3, tHREe) (3, tHREE)
(3, Three) (3, ThreE) (3, ThrEe) (3, ThrEE) (3, ThRee) (3, ThReE) (3, ThREe) (3, ThREE)
(3, THree) (3, THreE) (3, THrEe) (3, THrEE) (3, THRee) (3, THReE) (3, THREe) (3, THREE)
</pre></blockquote>

<p>
That is, all existing implementations implement "insert without hint" as "insert
at upper bound."
</p>

<p>
Therefore this paper proposes to standardize existing practice to the benefit of
clients by guaranteeing that the "insert without hint" functions of the
associative containers append to any existing <tt>equal_range</tt> in the
container.
</p>

<h3><a name="Efficiency Concerns"></a>Efficiency Concerns</h3>

<p>
One understandable reaction to the "as close to the hint as possible" suggestion
was a concern that it would cause undue inefficiency either computationally, or
in code size.  However, this concern turned out to be unwarranted.  The
suggestion can be simply and efficiently implemented with the following
algorithm (on the left) which makes use of the existing <tt>lower_bound</tt> and
<tt>upper_bound</tt> logic.  The existing "ignore hint if wrong" algorithm is
presented on the right for comparison.  The number of comparisons required to
decide if the hint is valid is also listed for each algorithm.
</p>

<p><center>
<table border="1" cellpadding="10">
<caption>Insert <tt>x</tt> at <tt>p</tt> Algorithms<br>only check <i>before</i></caption>
<tr><th>insert as close to hint as possible</th> <th>ignore hint if wrong</th></tr>
<tr>
<td>
<pre>
To insert x at p:
   if p == end || x &lt;= *p
      if p == begin || x &gt;= *(p-1)
         insert x before p
      else
         insert x before upper_bound(x)
   else
      insert x before lower_bound(x)
</pre>
</td>
<td>
<pre>
To insert x at p:
   if (p == end   || x &lt;= *p) &&
      (p == begin || x &gt;= *(p-1))
      insert x before p
   else
      insert x without hint
</pre>
</td>
</tr>
<tr>
<td align="center">1 or 2 comparisons executed</td>
<td align="center">1 or 2 comparisons executed</td>
</tr>
</table>
</center></p>

<p>
The logic for the two algorithms is very similar, requiring exactly the same
tests.  The only difference is the use of <tt>lower_bound</tt> or
<tt>upper_bound</tt> to guide insertion instead of "insert without hint" when
<tt>x &gt; *p</tt> or when <tt>x &lt; *(p-1)</tt> respectively.
</p>

<p>
For those implementations wanting to include an <i>after</i> check, the above
algorithms can be slightly modified to accommodate.  Note that this extra check
will only affect complexity, and not the relative order within an
<tt>equal_range</tt> (for the left hand algorithm). Once the <i>after</i>
position is in need of checking, the algorithm on the left is going to place
<tt>x</tt> at <tt>lower_bound(x)</tt>, and the only question that remains is
whether it will be done with constant or <tt>Log(N)</tt> complexity.
</p>

<p><center>
<table border="1" cellpadding="10">
<caption>Insert <tt>x</tt> at <tt>p</tt> Algorithms<br>
first check <i>before</i> then check <i>after</i></caption>
<tr><th>insert as close to hint as possible</th> <th>ignore hint if wrong</th></tr>
<tr>
<td>
<pre>
To insert x at p:
   if p == end || x &lt;= *p
      if p == begin || x &gt;= *(p-1)
         insert x before p
      else
         insert x before upper_bound(x)
   else if p+1 == end || x &lt;= *(p+1)
      insert x after p
   else
      insert x before lower_bound(x)
</pre>
</td>
<td>
<pre>
To insert x at p:
   if p == end || x &lt;= *p
      if p == begin || x &gt;= *(p-1)
         insert x before p
      else
         insert x without hint
   else if p+1 == end || x &lt;= *(p+1)
      insert x after p
   else
      insert x without hint
</pre>
</td>
</tr>
<tr>
<td align="center">2 comparisons executed</td>
<td align="center">2 comparisons executed</td>
</tr>
</table>
</center></p>

<p>
In either case (with or without the <i>after</i> check), the "insert as close to
hint as possible" algorithm is not significantly more complicated nor any less
efficient than the "ignore hint if wrong" algorithm (same number of calls to the
comparison operator and same complexity in each case).
</p>

<p>
The differences between the algorithms with and without the "as close as
possible to hint" rule are independent of the type of tree.  That is, there is
nothing red-black specific that is being proposed.  The only change in the
algorithms is the use of (insert at) <tt>lower_bound</tt> and
<tt>upper_bound</tt> in place of "insert without hint".
</p>

<p>
There may still be a concern that "insert without hint" can be cheaper than
"insert before upper_bound" or "insert before lower_bound".  However the
<a href="#Insert without hint">previous section</a> in this paper argues
that with respect to all current implementations "insert without hint" and
"insert before upper_bound" are exactly the same thing.  Furthermore the
Coren/Leiserson/Rivest algorithm for inserting (at upper bound) can be trivially
modified to insert at the lower bound instead:
</p>

<blockquote><pre>
Tree-Insert(T, z)
   y &lt;- NIL
   x &lt;- root[T]
   while x != NIL
      do y &lt;- x
         if key[z] <b>&lt;=</b> key[x]
            then x &lt;- left[x]
            else x &lt;- right[x]
   p[z] &lt;- y
   if y == NIL
      then root[T] &lt;- z
      else if key[z] <b>&lt;=</b> key[y]
         then left[y] &lt;- z
         else right[y] &lt;- z
</pre></blockquote>

<p>
Therefore this paper asserts that there is absolutely no extra computational
expense proposed herein.
</p>

<p>
Note:  The "as close as possible to hint" rule has no effect on the algorithms
used to insert into containers with unique keys.  When <tt>x</tt> does not yet
exist in a container, <tt>lower_bound</tt>, <tt>upper_bound</tt> and "insert
without hint" all refer to the same location.  And when <tt>x</tt> does already
exist in a unique-key container, no insertion is performed.
</p>

<h3><a name="Prior Art"></a>Prior Art</h3>

<p>
Metrowerks has implemented this proposal (with the additional <i>after</i>
check) since 2001.
</p>

<h3><a name="Summary"></a>Summary</h3>

<p>
This proposal seeks deterministic and functional behavior which gives clients
complete control over the element order within an <tt>equal_range</tt> of a
<tt>multiset</tt> or <tt>multimap</tt>, in some cases providing a significant
computational savings, and in no case burdening the client with additional
expense.  Such control may enable clients to use these standard facilities in
more situations, and in more efficient ways.
</p>

<h3><a name="Proposed Wording"></a>Proposed Wording</h3>

<h4>23.1.2 - Associative containers</h4>

<p>Paragraph 7:</p>

<p><center>
<table border="1">
<caption>Associative container requirements</caption>
<tr><th>expression</th> <th>return type</th>
<th>assertion/note<br>pre/post-condition</th>
<th>complexity</th></tr>
<tr><td><tt>a_eq.insert(t)</tt></td>
<td><tt>iterator</tt></td>
<td>
inserts <tt>t</tt> and returns the iterator pointing to the newly inserted element.
<ins>If an <tt>equal_range</tt> equivalent to <tt>t</tt> already exists, <tt>t</tt>
is inserted at the <tt>upper_bound</tt> of that <tt>equal_range</tt>.</ins>
</td>
<td>
logarithmic
</td></tr>
<tr><td><tt>a.insert(p,t)</tt></td>
<td><tt>iterator</tt></td>
<td>
inserts <tt>t</tt> if and only if there is no element with key equivalent to the
key of <tt>t</tt> in containers with unique keys; always inserts <tt>t</tt> in containers
with equivalent keys. always returns the iterator pointing to the element with key
equivalent to the key of <tt>t</tt>. <del>iterator <tt>p</tt> is a hint pointing to where
the insert should start to search.</del> <ins><tt>t</tt> is inserted as close as possible
to the position just prior to <tt>p</tt>.</ins>
</td>
<td>
logarithmic in general, but amortized constant if <tt>t</tt> is inserted right <del>after</del>
 <ins>before</ins> <tt>p</tt>.
</td></tr>
</table>
</center></p>

<p>Paragraph 8:</p>

<p>
-8- The insert members shall not affect the validity of iterators and references
to the container, and the erase members shall invalidate only iterators and
references to the erased elements. <ins>The relative ordering of elements in the
container is stable throughout the lifetime of the container, even within an
<tt>equal_range</tt>, unless explicitly changed by the client with
<tt>erase</tt> and <tt>insert</tt>.</ins>
</p>

<h2><a name="Acknowledments"></a>Acknowledments</h2>

<p>
Thanks to Andrew Koenig for submitting
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#233">LWG
issue 233</a>
in the first place, and for originally suggesting the "as close to hint as
possible" feature. Thanks also to John Potter who showed how the "insert as
close to hint as possible" rule is not nearly as complicated or expensive as I
had first feared.
</p>

<p>
Thanks to Alberto Barbati, Jerry Coffin, Beman Dawes, Raoul Gough, Russell Hind,
Christopher Jefferson, Bronek Kozicki, Daniel Kr&uuml;gler, Branimir Maksimovic,
Guy Middleton, Nicola Musatti, John Potter, Larry I Smith and Maxim Yegorushkin
for helping with the survey of current practice.
</p>

<p>
This paper acknowledges the work of the prior LWG issue
<a
href="http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#192">192</a>
which was pretty much right on target, just ahead of its time.
</p>

</body>
</html>
