<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 2469: Wrong specification of Requires clause of operator[] for map and unordered_map</title>
<meta property="og:title" content="Issue 2469: Wrong specification of Requires clause of operator[] for map and unordered_map">
<meta property="og:description" content="C++ library issue. Status: C++17">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue2469.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#C++17">C++17</a> status.</em></p>
<h3 id="2469"><a href="lwg-defects.html#2469">2469</a>. Wrong specification of Requires clause of <code>operator[]</code> for <code>map</code> and <code>unordered_map</code></h3>
<p><b>Section:</b> 23.4.3.3 <a href="https://wg21.link/map.access">[map.access]</a>, 23.5.3.3 <a href="https://wg21.link/unord.map.elem">[unord.map.elem]</a> <b>Status:</b> <a href="lwg-active.html#C++17">C++17</a>
 <b>Submitter:</b> Tomasz Kami&nacute;ski <b>Opened:</b> 2015-01-21 <b>Last modified:</b> 2017-07-30</p>
<p><b>Priority: </b>3
</p>
<p><b>View all other</b> <a href="lwg-index.html#map.access">issues</a> in [map.access].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#C++17">C++17</a> status.</p>
<p><b>Discussion:</b></p>
<p>
The "Requires:" clause for the <code>operator[]</code> for the <code>unordered_map</code> and <code>map</code>, are defining 
separate requirements for insertability into container of <code>mapped_type</code> and <code>key_type</code>.
<p/>
23.4.3.3 <a href="https://wg21.link/map.access">[map.access]</a> p2: // <code>T&amp; operator[](const key_type&amp; x);</code>
<p/>
<i>Requires</i>: <code>key_type</code> shall be <code>CopyInsertable</code> and <code>mapped_type</code> shall be <code>DefaultInsertable</code> 
into <code>*this</code>.
<p/>
23.4.3.3 <a href="https://wg21.link/map.access">[map.access]</a> p6: // <code>T&amp; operator[](key_type&amp;&amp; x)</code>
<p/>
<i>Requires</i>: <code>mapped_type</code> shall be <code>DefaultInsertable</code> into <code>*this</code>.
<p/>
23.5.3.3 <a href="https://wg21.link/unord.map.elem">[unord.map.elem]</a> p1: // <code>mapped_type&amp; operator[](const key_type&amp; k); 
mapped_type&amp; operator[](key_type&amp;&amp; k);</code>
<p/>
<i>Requires</i>: <code>mapped_type</code> shall be <code>DefaultInsertable</code> into <code>*this</code>. For the first operator, 
<code>key_type</code> shall be <code>CopyInsertable</code> into <code>*this</code>. For the second operator, <code>key_type</code> shall 
be <code>MoveConstructible</code>.
<p/>
Definition of the appropriate requirements: 23.2.2 <a href="https://wg21.link/container.requirements.general">[container.requirements.general]</a> p15.
<p/>
<code>T</code> is <code>DefaultInsertable</code> into <code>X</code> means that the following expression is well-formed: //p15.1
<p/>
<code>allocator_traits&lt;A&gt;::construct(m, p)</code>
<p/>
<code>T</code> is <code>MoveInsertable</code> into <code>X</code> means that the following expression is well-formed:   //p15.3
<p/>
<code>allocator_traits&lt;A&gt;::construct(m, p, rv)</code>
<p/>
<code>T</code> is <code>CopyInsertable</code> into <code>X</code> means that, in addition to <code>T</code> being <code>MoveInsertable</code> 
into <code>X</code>, the following expression is well-formed: //p15.4
<p/>
<code>allocator_traits&lt;A&gt;::construct(m, p, v)</code>
<p/>
In the context of above definition the requirement "<code>key_type</code> shall be <code>CopyInsertable</code> into <code>*this</code>" 
would mean that the key element of the <code>pair&lt;const key_type, mapped_type&gt;</code> (<code>value_type</code> of the map) 
should be constructed using separate call to the <code>construct</code> method, the same applies for the <code>mapped_type</code>. 
Such behavior is explicitly prohibited by 23.2.2 <a href="https://wg21.link/container.requirements.general">[container.requirements.general]</a> p3.
</p>
<blockquote><p>
For the components affected by this sub-clause that declare an allocator_type, objects stored in these 
components shall be constructed using the <code>allocator_traits&lt;allocator_type&gt;::construct</code> function and
destroyed using the <code>allocator_traits&lt;allocator_type&gt;::destroy</code> function (20.7.8.2). These functions
are called only for the container's element type, not for internal types used by the container.
</p></blockquote>
<p>
It clearly states that <code>element_type</code> of the map, must be constructed using allocator for value type, which 
disallows using of separate construction of first and second element, regardless of the fact if it can be actually 
performed without causing undefined behavior.
<p/>
That means that the <code>MoveInsertable</code> and similar requirements may only be expressed in terms of <code>value_type</code>, 
not its members types.
</p>

<p><i>[2015-02 Cologne]</i></p>

<p>
This issue is related to <a href="lwg-defects.html#2464" title="try_emplace and insert_or_assign misspecified (Status: C++17)">2464</a><sup><a href="https://cplusplus.github.io/LWG/issue2464" title="Latest snapshot">(i)</a></sup>.
<p/>
GR: Effects should say "returns ...". DK: Or just have a Returns clause? MC: A Returns clause is a directive to implementers.
<p/>
TK/DK: This PR fails to address the requirements about which it complained in the first place. DK: I can reword this. TK can help. 
</p> 

<p><i>[2015-03-29, Daniel provides improved wording]</i></p>

<p>
The revised wording fixes the proper usage of the magic "Equivalent to" wording, which automatically induces <i>Requires:</i>, 
<i>Returns:</i>, and <i>Complexity:</i> elements (and possibly more). This allows us to strike all the remaining
elements, because they fall out from the semantics of the wording defined by <a href="lwg-defects.html#2464" title="try_emplace and insert_or_assign misspecified (Status: C++17)">2464</a><sup><a href="https://cplusplus.github.io/LWG/issue2464" title="Latest snapshot">(i)</a></sup>. In particular it is important
to realize that the wording form
</p>
<blockquote>
<p>
<code>value_type</code> shall be <code>EmplaceConstructible</code> into <code>map</code> from <code>piecewise_construct</code>, 
<code>forward_as_tuple(k)</code>, <code>forward_as_tuple(forward&lt;Args&gt;(args)...)</code>
</p>
</blockquote>
<p>
degenerates for the empty pack expansion <code>args</code> to:
</p>
<blockquote>
<p>
<code>value_type</code> shall be <code>EmplaceConstructible</code> into <code>map</code> from <code>piecewise_construct</code>, 
<code>forward_as_tuple(k)</code>, <code>forward_as_tuple()</code>
</p>
</blockquote>
<p>
which again means that such a <code>pair</code> construction (assuming <code>std::allocator</code>) would copy <code>k</code>
into member <code>first</code> and would value-initialize member <code>second</code>.
</p>

<p>
<strong>Previous resolution [SUPERSEDED]:</strong>
</p>
<blockquote class="note">
<p>This wording is relative to N4296.</p>

<p>Accept resolution of the issue issue <a href="lwg-defects.html#2464" title="try_emplace and insert_or_assign misspecified (Status: C++17)">2464</a><sup><a href="https://cplusplus.github.io/LWG/issue2464" title="Latest snapshot">(i)</a></sup> and define <code>operator[]</code> as follows 
(This would also address issue <a href="lwg-defects.html#2274" title="Does map::operator[] value-initialize or default-insert a missing element? (Status: Resolved)">2274</a><sup><a href="https://cplusplus.github.io/LWG/issue2274" title="Latest snapshot">(i)</a></sup>):</p>

<ol>
<li><p>Change 23.4.3.3 <a href="https://wg21.link/map.access">[map.access]</a> as indicated:</p>

<blockquote>
<pre>
T&amp; operator[](const key_type&amp; x);
</pre>
<blockquote><p>
-1- <i>Effects</i>: <del>If there is no key equivalent to <code>x</code> in the map, inserts <code>value_type(x, T())</code> 
into the map</del><ins>Equivalent to: <code>try_emplace(x).first-&gt;second</code></ins>.
<p/>
[&hellip;]
</p>
</blockquote>
<pre>
T&amp; operator[](key_type&amp;&amp; x);
</pre>
<blockquote>
<p>
-5- <i>Effects</i>: <del>If there is no key equivalent to <code>x</code> in the map, inserts <code>value_type(std::move(x), T())</code> 
into the map</del><ins>Equivalent to: <code>try_emplace(move(x)).first-&gt;second</code></ins>.
</p>
</blockquote>
</blockquote>
</li>

<li><p>Change 23.5.3.3 <a href="https://wg21.link/unord.map.elem">[unord.map.elem]</a> as indicated:</p>

<blockquote>
<pre>
mapped_type&amp; operator[](const key_type&amp; k);
mapped_type&amp; operator[](key_type&amp;&amp; k);
</pre>
<blockquote>
<p>
[&hellip;]
<p/>
-2- <i>Effects</i>: <del>If the <code>unordered_map</code> does not already contain an element whose key is equivalent to <code>k</code>, 
the first operator inserts the value <code>value_type(k, mapped_type())</code> and the second operator inserts the
value <code>value_type(std::move(k), mapped_type())</code></del><ins>For the first operator, equivalent to: 
<code>try_emplace(k).first-&gt;second</code>; for the second operator, equivalent to: 
<code>try_emplace(move(k)).first-&gt;second</code></ins>.
</p>
</blockquote>
</blockquote>
</li>
</ol>
</blockquote>

<p>
<strong>Previous resolution [SUPERSEDED]:</strong>
</p>
<blockquote class="note">
<p>This wording is relative to N4296.</p>

<p>Accept resolution of the issue issue <a href="lwg-defects.html#2464" title="try_emplace and insert_or_assign misspecified (Status: C++17)">2464</a><sup><a href="https://cplusplus.github.io/LWG/issue2464" title="Latest snapshot">(i)</a></sup> and define <code>operator[]</code> as follows 
(This would also address issue <a href="lwg-defects.html#2274" title="Does map::operator[] value-initialize or default-insert a missing element? (Status: Resolved)">2274</a><sup><a href="https://cplusplus.github.io/LWG/issue2274" title="Latest snapshot">(i)</a></sup>):</p>

<ol>
<li><p>Change 23.4.3.3 <a href="https://wg21.link/map.access">[map.access]</a> as indicated:</p>

<blockquote>
<pre>
T&amp; operator[](const key_type&amp; x);
</pre>
<blockquote><p>
-1- <i>Effects</i>: <del>If there is no key equivalent to <code>x</code> in the map, inserts <code>value_type(x, T())</code> 
into the map.</del><ins>Equivalent to: <code>return try_emplace(x).first-&gt;second;</code></ins>
<p/>
<del>-2- <i>Requires</i>: <code>key_type</code> shall be <code>CopyInsertable</code> and <code>mapped_type</code> shall be 
<code>DefaultInsertable</code> into <code>*this</code>.</del>
<p/>
<del>-3- <i>Returns</i>: A reference to the <code>mapped_type</code> corresponding to <code>x</code> in <code>*this</code>.</del>
<p/>
<del>-4- <i>Complexity</i>: Logarithmic.</del>
</p>
</blockquote>
<pre>
T&amp; operator[](key_type&amp;&amp; x);
</pre>
<blockquote>
<p>
-5- <i>Effects</i>: <del>If there is no key equivalent to <code>x</code> in the map, inserts <code>value_type(std::move(x), T())</code> 
into the map.</del><ins>Equivalent to: <code>return try_emplace(move(x)).first-&gt;second;</code></ins>
<p/>
<del>-6- <i>Requires</i>: <code>mapped_type</code> shall be <code>DefaultInsertable</code> into <code>*this</code>.</del>
<p/>
<del>-7- <i>Returns</i>: A reference to the <code>mapped_type</code> corresponding to <code>x</code> in <code>*this</code>.</del>
<p/>
<del>-8- <i>Complexity</i>: Logarithmic.</del>
</p>
</blockquote>
</blockquote>
</li>

<li><p>Change 23.5.3.3 <a href="https://wg21.link/unord.map.elem">[unord.map.elem]</a> as indicated:</p>

<blockquote>
<pre>
mapped_type&amp; operator[](const key_type&amp; k);
mapped_type&amp; operator[](key_type&amp;&amp; k);
</pre>
<blockquote>
<p>
<del>-1- <i>Requires</i>: <code>mapped_type</code> shall be <code>DefaultInsertable</code> into <code>*this</code>. For the first operator, 
<code>key_type</code> shall be <code>CopyInsertable</code> into <code>*this</code>. For the second operator, <code>key_type</code> shall 
be <code>MoveConstructible</code>.</del>
<p/>
-2- <i>Effects</i>: <del>If the <code>unordered_map</code> does not already contain an element whose key is equivalent to <code>k</code>, 
the first operator inserts the value <code>value_type(k, mapped_type())</code> and the second operator inserts the
value <code>value_type(std::move(k), mapped_type())</code></del><ins>For the first operator, equivalent to:</ins>
</p>
<blockquote>
<pre> 
<ins>return try_emplace(k).first-&gt;second;</ins>
</pre>
</blockquote>
<p>
<ins>for the second operator, equivalent to:</ins>
</p>
<blockquote>
<pre>
<ins>return try_emplace(move(k)).first-&gt;second;</ins>
</pre>
</blockquote>
<p>
<del>-3- <i>Returns</i>: A reference to <code>x.second</code>, where <code>x</code> is the (unique) element whose key is equivalent to 
<code>k</code>.</del>
<p/>
<del>-4- <i>Complexity</i>: Average case &#x1d4aa;(<code>1</code>), worst case &#x1d4aa;(<code>size()</code>).</del>
</p>
</blockquote>
</blockquote>
</li>
</ol>
</blockquote>


<p id="res-2469"><b>Proposed resolution:</b></p>
<p>This wording is relative to N4431.</p>

<p>Accept resolution of the issue issue <a href="lwg-defects.html#2464" title="try_emplace and insert_or_assign misspecified (Status: C++17)">2464</a><sup><a href="https://cplusplus.github.io/LWG/issue2464" title="Latest snapshot">(i)</a></sup> and define <code>operator[]</code> as follows 
(This would also address issue <a href="lwg-defects.html#2274" title="Does map::operator[] value-initialize or default-insert a missing element? (Status: Resolved)">2274</a><sup><a href="https://cplusplus.github.io/LWG/issue2274" title="Latest snapshot">(i)</a></sup>):</p>

<ol>
<li><p>Change 23.4.3.3 <a href="https://wg21.link/map.access">[map.access]</a> as indicated:</p>

<blockquote>
<pre>
T&amp; operator[](const key_type&amp; x);
</pre>
<blockquote><p>
-1- <i>Effects</i>: <del>If there is no key equivalent to <code>x</code> in the map, inserts <code>value_type(x, T())</code> 
into the map.</del><ins>Equivalent to: <code>return try_emplace(x).first-&gt;second;</code></ins>
<p/>
<del>-2- <i>Requires</i>: <code>key_type</code> shall be <code>CopyInsertable</code> and <code>mapped_type</code> shall be 
<code>DefaultInsertable</code> into <code>*this</code>.</del>
<p/>
<del>-3- <i>Returns</i>: A reference to the <code>mapped_type</code> corresponding to <code>x</code> in <code>*this</code>.</del>
<p/>
<del>-4- <i>Complexity</i>: Logarithmic.</del>
</p>
</blockquote>
<pre>
T&amp; operator[](key_type&amp;&amp; x);
</pre>
<blockquote>
<p>
-5- <i>Effects</i>: <del>If there is no key equivalent to <code>x</code> in the map, inserts <code>value_type(std::move(x), T())</code> 
into the map.</del><ins>Equivalent to: <code>return try_emplace(move(x)).first-&gt;second;</code></ins>
<p/>
<del>-6- <i>Requires</i>: <code>mapped_type</code> shall be <code>DefaultInsertable</code> into <code>*this</code>.</del>
<p/>
<del>-7- <i>Returns</i>: A reference to the <code>mapped_type</code> corresponding to <code>x</code> in <code>*this</code>.</del>
<p/>
<del>-8- <i>Complexity</i>: Logarithmic.</del>
</p>
</blockquote>
</blockquote>
</li>

<li><p>Change 23.5.3.3 <a href="https://wg21.link/unord.map.elem">[unord.map.elem]</a> as indicated:</p>

<blockquote>
<pre>
mapped_type&amp; operator[](const key_type&amp; k);
<del>mapped_type&amp; operator[](key_type&amp;&amp; k);</del>
</pre>
<blockquote>
<p>
<del>-1- <i>Requires</i>: <code>mapped_type</code> shall be <code>DefaultInsertable</code> into <code>*this</code>. For the first operator, 
<code>key_type</code> shall be <code>CopyInsertable</code> into <code>*this</code>. For the second operator, <code>key_type</code> shall 
be <code>MoveConstructible</code>.</del>
<p/>
-2- <i>Effects</i>: <ins>Equivalent to <code>return try_emplace(k).first-&gt;second;</code></ins><del>If the <code>unordered_map</code> 
does not already contain an element whose key is equivalent to <code>k</code>, 
the first operator inserts the value <code>value_type(k, mapped_type())</code> and the second operator inserts the
value <code>value_type(std::move(k), mapped_type())</code></del>
</p>
<p>
<del>-3- <i>Returns</i>: A reference to <code>x.second</code>, where <code>x</code> is the (unique) element whose key is equivalent to 
<code>k</code>.</del>
<p/>
<del>-4- <i>Complexity</i>: Average case &#x1d4aa;(<code>1</code>), worst case &#x1d4aa;(<code>size()</code>).</del>
</p>
</blockquote>

<pre>
<ins>mapped_type&amp; operator[](key_type&amp;&amp; k);</ins>
</pre>
<blockquote>
<p>
<ins>-?- <i>Effects</i>: Equivalent to <code>return try_emplace(move(k)).first-&gt;second;</code></ins>
</p>
</blockquote>

</blockquote>
</li>
</ol>





</body>
</html>
