Reputation: 13398
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?
Upvotes: 3
Views: 133
Reputation: 33932
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
q
denotes a valid dereferenceable constant iterator toa
,r
denotes 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.
Upvotes: 5
Reputation: 4833
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.
Upvotes: 4