Take this snippet of C++:
#include <map>
int main() {
std::map<int, int> m1;
m1[1] = 2;
std::map<int, int> m2;
m2[3] = 4;
m1.erase(m2.begin());
return m2.size();
}
On godbolt: https://godbolt.org/z/mJBszn
This feels like it must be undefined behaviour. Is that correct? If so, which part of the standard says so?
This feels like it must be undefined behaviour. Is that correct?
Yes.
If so, which part of the standard says so?
The standard slaps down this bit of silliness in [associative.reqmts] note 8. I am citing n4659 because it's what I have a link to and close to C++17. At this time C++20 is still too much of a moving target.
Diving down into [tab:container.assoc.req] we find three erase overloads that take iterators,
a.erase(q)
a.erase(r)
a.erase(q1, q2)
of which a.erase(r) is the one of interest to the asker.
The table only states what happens if the program behaves; however the preamble to this table states that
qdenotes a valid dereferenceable constant iterator toa,rdenotes a valid dereferenceable iterator toa, [q1,q2) denotes a valid range of constant iterators ina
In other words, if the iterator r is not from map a, a's end iterator, or has been invalidated or otherwise rendered undereferenceable, the contract is broken and the results have been left undefined.
I include q, q1 and q2 to show the rules are the same for a constant iterator and iterator ranges.
This is all in reference to C++17.
Edit 2** I take back what I originally said, I just thread through part of the c++ standard and from a comment. In §26.2.6 the standard states for a.erase(r) in the context of associative containers is "If no such element exists, returns a.end()." However, the standard also states "r denotes a valid dereferenceable iterator to a"
Since this is not the case for m2.begin(), this is not in line with the standard, thus undefined behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With