Removing Item from Vector, While in C++11 Range 'For' Loop

Removing item from vector, while in C++11 range 'for' loop?

No, you can't. Range-based for is for when you need to access each element of a container once.

You should use the normal for loop or one of its cousins if you need to modify the container as you go along, access an element more than once, or otherwise iterate in a non-linear fashion through the container.

For example:

auto i = std::begin(inv);

while (i != std::end(inv)) {
// Do some stuff
if (blah)
i = inv.erase(i);
else
++i;
}

how to erase from vector in range-based loop?

You can't erase elements by value on a std::vector, and since range-based loop expose directly values your code doesn't make sense (vec.erase(&i)).

The main problem is that a std::vector invalidates its iterators when you erase an element.

So since the range-based loop is basically implemented as

auto begin = vec.begin();
auto end = vec.end()
for (auto it = begin; it != end; ++it) {
..
}

Then erasing a value would invalidate it and break the successive iterations.

If you really want to remove an element while iterating you must take care of updating the iterator correctly:

for (auto it = vec.begin(); it != vec.end(); /* NOTHING */)
{
if ((*it) > 5)
it = vec.erase(it);
else
++it;
}

Can we erase the items in range-based for loop in c++11

That's not what the range-based loop is for. Don't use it; use a normal for loop instead. The range-based version is only if you want to do something with every element in the container, without mutating the container.

for (auto it = ss.begin(); it != ss.end(); )
{
if (*it < v) { ss.erase(it++); }
else { ++it; }
}

Even simpler:

ss.erase(ss.begin(), ss.lower_bound(v));

Erasing vector elements while in a range-based loop vs. standard loop

In the first case, for each iteration std::vector::size function is being called. Thus, if you delete all the elements in the first iteration, std::vector::size function which is called before the start of second iteration will return 0. Therefore, second iteration won't happen because the condition i < test.size() is not satisfied.

In the second case, range-based for loop uses iterators instead of std::vector::size function. When you call std::vector::erase you invalidate all the iterators including the end() iterator. Therefore, second case is actually UB (Undefined Behavior) and you should never rely on that.

From the docs:

std::vector::erase

... Invalidates iterators and references at or after the point of the
erase, including the end() iterator.

Removing item from vector, while in C++11 range 'for' loop?

No, you can't. Range-based for is for when you need to access each element of a container once.

You should use the normal for loop or one of its cousins if you need to modify the container as you go along, access an element more than once, or otherwise iterate in a non-linear fashion through the container.

For example:

auto i = std::begin(inv);

while (i != std::end(inv)) {
// Do some stuff
if (blah)
i = inv.erase(i);
else
++i;
}

Remove elements of a vector inside the loop

You should not increment it in the for loop:

for (vector<Player>::iterator it=allPlayers.begin(); 
it!=allPlayers.end();
/*it++*/) <----------- I commented it.
{

if(it->getpMoney()<=0)
it = allPlayers.erase(it);
else
++it;
}

Notice the commented part;it++ is not needed there, as it is getting incremented in the for-body itself.

As for the error "'operator =' function is unavailable in 'Player’", it comes from the usage of erase() which internally uses operator= to move elements in the vector. In order to use erase(), the objects of class Player must be assignable, which means you need to implement operator= for Player class.

Anyway, you should avoid raw loop1 as much as possible and should prefer to use algorithms instead. In this case, the popular Erase-Remove Idiom can simplify what you're doing.

allPlayers.erase(
std::remove_if(
allPlayers.begin(),
allPlayers.end(),
[](Player const & p) { return p.getpMoney() <= 0; }
),
allPlayers.end()
);

1. It's one of the best talks by Sean Parent that I've ever watched.

Removing non-prime numbers from a vector

const int p_limit = sqrt(foo.at(foo.size() - 1));

This will initialize the limit once, based on the last element in the list. You have to do that for each element being test for prime.

More importantly, limit_counter should be initialized for each i

Ignoring the problem with iterators, you can fix it with this pseudo code.

std::vector<int> prime_list(std::vector<int> foo) 
{
foo.erase(std::remove(foo.begin(), foo.end(), 0), foo.end());
foo.erase(std::remove(foo.begin(), foo.end(), 1), foo.end());
for (int i : foo)
{
int limit_counter = 1;
const int p_limit = static_cast<int>(sqrt(i));
do
{
if (i % limit_counter == 0)
{
//undefined behavior, this might destroy the iterator for `i`
foo.erase(std::remove(foo.begin(), foo.end(), i), foo.end());
}
limit_counter++;
} while (limit_counter < p_limit);
}
return foo;
}

For an easier and safer solution, just create a new list and add the primes to it. Or, create a duplicate list primes and go through the loop in foo

std::vector<int> prime_list(std::vector<int> &foo) 
{
std::vector<int> primes = foo;
primes.erase(std::remove(primes.begin(), primes.end(), 0), primes.end());
primes.erase(std::remove(primes.begin(), primes.end(), 1), primes.end());
for (int n : foo)
for (int k = 2, limit = static_cast<int>(sqrt(n)); k <= limit; k++)
if (n % k == 0)
primes.erase(std::remove(primes.begin(), primes.end(), n), primes.end());
return primes;
}


Related Topics



Leave a reply



Submit