Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I remove elements in the std::string object while iterating over it

Tags:

c++

Can I remove elements in the std::string object while iterating over it?

for (auto itr = str.rbegin(), rend = str.rend(); itr != str.rend() && *itr == '0'; ++itr)
{
    str.pop_back();
}
like image 547
FrozenHeart Avatar asked Sep 02 '25 13:09

FrozenHeart


2 Answers

No, that's not allowed. Modifying the contents of the string invalidates any iterators, particularly itr.

To remove trailing characters from a string, consider using find_last_not_of:

auto ix = str.find_last_not_of('0');
str.resize(ix + 1);

Another option is to use the erase function, which will return the next iterator in the sequence, thereby avoiding having any invalid iterators.

for (auto itr = str.rbegin(); itr != str.rend() && *itr == '0'; /*nothing*/)
  itr = str.erase(itr);

That will erase the last character (as pop_back would) and safely advance the iterator, so you never actually have an invalid iterator anymore. The caveat is that you cannot you the rend iterator that you were calculating before, because it would be invalid; however, you weren't actually using it anyway.

like image 68
Rob Kennedy Avatar answered Sep 05 '25 02:09

Rob Kennedy


You can remove elements as you iterate over the string, but you need to write your loop slightly differently to do it safely. In this case, we only really care about the last character in the string, so we can do something like this:

for (auto itr = str.rbegin(); 
          itr != str.rend() && *itr == '0'; 
          itr=str.rbegin())
{
    str.pop_back();
}

Even though itr may be invalidated when we do pop_back on the string, we're re-fetching the value of str.rbegin() every iteration, and that's guaranteed to give a valid reverse iterator every time we call it. What we have left no longer really makes good use of a for loop though--we might about as well use a while loop:

while (str.rbegin() != str.rend() && *str.rbegin() == '0')
    str.pop_back();

I think I'd rather write it something like:

while (!str.empty() && *str.rbegin() == '0')
    str.pop_back();

...or (using a slightly cleaner equivalent of *str.rbegin():

while (!str.empty() && str.back() == '0')
    str.pop_back();
like image 35
Jerry Coffin Avatar answered Sep 05 '25 04:09

Jerry Coffin