<html>
<head>
<base href="https://bugs.llvm.org/">
</head>
<body><table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Bug ID</th>
<td><a class="bz_bug_link
bz_status_NEW "
title="NEW - std::map:operator= does assignment on the underlying value type, dereferencing references!"
href="https://bugs.llvm.org/show_bug.cgi?id=47956">47956</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>std::map:operator= does assignment on the underlying value type, dereferencing references!
</td>
</tr>
<tr>
<th>Product</th>
<td>libc++
</td>
</tr>
<tr>
<th>Version</th>
<td>11.0
</td>
</tr>
<tr>
<th>Hardware</th>
<td>All
</td>
</tr>
<tr>
<th>OS</th>
<td>All
</td>
</tr>
<tr>
<th>Status</th>
<td>NEW
</td>
</tr>
<tr>
<th>Severity</th>
<td>normal
</td>
</tr>
<tr>
<th>Priority</th>
<td>P
</td>
</tr>
<tr>
<th>Component</th>
<td>All Bugs
</td>
</tr>
<tr>
<th>Assignee</th>
<td>unassignedclangbugs@nondot.org
</td>
</tr>
<tr>
<th>Reporter</th>
<td>greg@dignus.com
</td>
</tr>
<tr>
<th>CC</th>
<td>llvm-bugs@lists.llvm.org, mclow.lists@gmail.com
</td>
</tr></table>
<p>
<div>
<pre>libc++'s std::map::operator= implementation re-uses the existing tree nodes in
the destination map to avoid unnecessary memory allocations. To accomplish
this, it uses assignment to overwrite the nodes' contents. If the value type
(template argument to std::map) is a reference, then that assignment is
__value_type::operator=, which calls std::pair::operator=, which does an actual
value assignment on the reference. This has two downsides: it means a "const
type &" reference can't be used for the value, and it means that it will
overwrite the value itself for mutable references!
Here is an example:
#include <map>
#include <iostream>
int arr[5] = {100,101,102,103,104};
typedef std::map<int, int&> m;
typedef m::value_type p;
int main(void) {
m a,b;
a.insert({ p(200,arr[0]), p(201,arr[1]), p(202,arr[2]),
p(203,arr[3]), p(204,arr[4]) });
b = a;
a = b; /* a's old nodes are now being overwritten! */
for (m::const_iterator i = a.begin(); i != a.end(); i++) {
std::cout<<i->first<<" "<<i->second<<" "<<
((int *)&i->second - arr)<<std::endl;
}
}
I have used that with libc++ versions 3.8.0, 9.0, and 11.0, and this is the
result I get from all versions:
200 100 0
201 101 2
202 101 4
203 103 3
204 101 1
The original arr[] elements have been overwritten. Most operations on the map
initialize the reference (taking the address of the value), but map assignment
assigns the reference (dereferencing it).
This result is astonishing to the user, but I'm not certain it's forbidden by
the standard. std::pair::operator= is required to do a value assignment, but
the requirements for containers are not as clear to me. Anyways, I'm quite
confident that this isn't the result a user would expect or desire.
I have a patch against version 3.8.0 that "fixes" the problem by implementing
__value_type::operator= in <map> using placement new (initializing the
reference) instead of assignment:
__value_type& operator=(const __value_type& __v)
{ __cc.~value_type(); new((void*)&__cc) value_type(__v.__cc);
return *this; }
But I don't really want to hold that up as an ideal fix, it's just the hack I
came up with. I figure there is a reason __value_type contorts the way that it
does, but I don't know it.</pre>
</div>
</p>
<hr>
<span>You are receiving this mail because:</span>
<ul>
<li>You are on the CC list for the bug.</li>
</ul>
</body>
</html>