Erasing vector::end from vector
Asked Answered
P

4

50

Does it works correctly (does nothing) when I use

 vector<T> v;
 v.erase(v.end());

I want to use something like

 v.erase(std::find(...));

Should I if is it v.end() or not?
There is no info about it on C++.com and CPPreference

Paley answered 6/3, 2012 at 19:1 Comment(0)
L
45

The standard doesn't quite spell it out, but v.erase(q) is defined, "Erases the element pointed to by q" in [sequence.reqmts]. This means that q must actually point to an element, which the end iterator doesn't. Passing in the end iterator is undefined behavior.

Unfortunately, you need to write:

auto it = std::find(...);
if (it != <the part of ... that specifies the end of the range searched>) {
    v.erase(it);
}

Of course, you could define:

template typename<Sequence, Iterator>
Iterator my_erase(Sequence &s, Iterator it) {
    if (it == s.end()) return it;
    return s.erase(it);
}

my_erase(v, std::find(v.begin(), v.end(), whatever));

c.erase() on an associative container returns void, so to generalize this template to all containers you need some -> decltype action.

Latrice answered 6/3, 2012 at 19:11 Comment(3)
docs says that " erasing elements in positions other than the vector end causes the container to relocate...". It looks like alowing of end() as parameter. And nowhere said the opposite explicitly. I dont like this...Mavis
@Pavel: then you'll have to take it up with the authors of "cplusplus.com". It is not the C++ documentation, the standard is the C++ documentation. But it defines position as "Iterator pointing to a single element". An end iterator does not point to a single element.Latrice
@Mavis They've got it wrong. It should say end() - 1 instead of "vector end".Talos
U
31

Erasing end() (or for that matter, even looking at the target of end()) is undefined behavior. Undefined behavior is allowed to have any behavior, including "just work" on your platform. That doesn't mean that you should be doing it; it's still undefined behavior, and I'll come bite you in the worst ways when you're least expecting it later on.

Depending on what you're doing, you might want to consider set or unordered_set instead of vector here.

Unstop answered 6/3, 2012 at 19:6 Comment(7)
thanks, i do know what is UB, I just was wanted to know, is it really UB.Paley
@RiaD: Yes, it is UB. The solution is very simple, though, just check before you erase: { auto it = v.find(x); if (it != x.end()) { v.erase(it); } }Hardcastle
Question for you @Billy. Out of curiosity, does end()-1 work? How is this different from pop_back()?From
@Gaffi: end-1 will work if and only if the container is not empty. (Same as pop_back)Unstop
@KerrekSB, yes I know I can check, it's quite easy:D. It just some ugly and I was thinking about replacing it:)Paley
@Billy, set and even multiset is not OK for me. deletion will very rare operation.Paley
@RiaD: Ok; just wanted to throw it out there. (Most often when I see people asking for this they're treating vectors as sets)Unstop
F
7

Have you tried this?

v.erase(remove_if(v.begin(), v.end(), (<your criteria>)), v.end());
From answered 6/3, 2012 at 19:4 Comment(9)
Lack of other correct answers does not make your answer correct.Unstop
I don't know why this has been downvoted (other than maybe an initial answer that has been edited). The code as it stands is correct.Tysontyumen
@DavidRodríguez-dribeas: As originally posted, it was not correct. Now that it has been edited to be correct, I have removed my downvote.Unstop
i needn't remove_if, I guessPaley
It'll delete ALL elements with <criteria>, not first onlyPaley
What is your criteria? What it is you are trying to std::find()?From
It's not clear to me why we believe this to be safe while vec.erase(vec.end()) clearly isn'tCassicassia
Like why should vec.erase(vec.end(), vec.end()); be ok when vec.erase(vec.end()) isn'tCassicassia
@Cassicassia Because vec.erase(vec.end(), vec.end()) erases a range (that happens to be zero-sized and so refers to zero items). vec.erase(vec.end()) attempts to erase an item that doesn't exist.Never
B
0

Since C++ 17, if statements can have an init statement, so erasing an element that may not be present can be simplified. As a bonus, the variable used for the iterator will not be visible outside the scope of the if statement.

if (auto it = std::find(v.begin(), v.end(), value); it != v.end()) v.erase(it);

In addition, std::ranges::find (or std::ranges::find_if) can be used since C++ 20, which don't require passing in begin() and end() iterators.

if (auto it = std::ranges::find(v, value); it != v.end()) v.erase(it);

The above solutions remove the first occurrence of value if it exists. To remove all occurrences of a value, std::erase from <vector> may be used since C++ 20.

std::erase(v, value);
Brunson answered 12/7, 2024 at 19:30 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.