简体   繁体   中英

Delete elements in vector by position?

In my class I am trying to remove an element from a std::vector using a for loop. However, when I try to remove the element, I receive an error and I am not quite sure how to solve it. The error I get is:

Error 4 error C2679: binary '+' : no operator found which takes a right-hand operand of type 'Enemy *' (or there is no acceptable conversion)

void Enemy::UpdateEnemies(SDL_Renderer * render)
{
    for (int i = enemies.size() - 1; i >= 0; i--)
    {
        enemies[i]->Update();
        if (enemies[i]->Active == false)
        {
            // Receive the error here
            enemies.erase(enemies.begin() + enemies.at(i));             
        }
    }
    if ((SDL_GetTicks()-prevSpawnTime)/1000.0f > enemySpawnTime)
    {
        prevSpawnTime = SDL_GetTicks();
        //Add an anemy
        AddEnemy(render);
    }    
}

Assuming that you want to remove the i -th element, you need to do

enemies.erase(enemies.begin() + i);

or better

enemies.erase(std::next(enemies.begin(), i));

In your case enemies.at(i) returns the dereferenced iterator at position i , ie an element of type Enemy , and not i or the iterator for position i .

A better way is to use reverse iterators:

for(auto it = enemies.rbegin(); it != enemies.rend(); ++it)
{
    (*it)->Update();
    if ((*it)->Active == false)
    {   // need `base()` to convert to regular iterator, then substract 1
        enemies.erase(std::prev(it.base())); // remove the current position
        /* or, equivalently
        enemies.erase(std::next(it).base()); 
        */
    }
}

Other answers have given you the naive solution. However, if you have more than one enemy to remove, you need a better solution.

Using std::remove_if from <algorithm> would be better in this case. That will avoid repeated shuffling of the items in your vector. It works by moving all the ones you want to remove to the end of the container, and then giving you an iterator to the beginning of those.

auto removed_iter = std::remove_if( enemies.begin(), enemies.end(),
                                    []( const Enemy * e ) { return e->IsActive(); } );

enemies.erase( removed_iter, enemies.end() );

In this case you would have to update all your enemies first. If that doesn't need to be done in reverse order, then:

for( auto e : enemies ) e->Update();

This line:

enemies.erase(enemies.begin() + enemies.at(i));
  • enemise.at(i) returns the Enemy that is stored in the vector.
  • enemies.begin() is a pointer

As the error says, you are trying to add pointer and vector.

You probably want just to call:

enemies.erase(enemies.begin() + i);

In addition to what others have said, I would take it a step further and suggest that erasing from a vector within a loop could cause undefined behavior. I don't see a break, so I assume that there could be multiple inactive enemies. Instead of writing your own loop, consider using the std::remove_if algorithm. Essentially your code is an attempt to add an iterator with an object reference which will fail to compile. The remove_if solution will essentially copy all enemies where Active==false to the end of the container while shifting everything else forward. It provides a convenient way to first identify the things to remove, and then erase them all at once. Additionally if you don't have a C++11 compiler the same thing will work if you use a different kind of predicate. The remove_if link contains an example of a function, but you can also use a functor.

enemies.erase(std::remove_if(enemies.begin(), enemies.end(), [](const Enemy* e){ return e->Active == false; }), enemies.end());

For more information check these out.

What is a lambda expression in C++11?

http://www.cplusplus.com/reference/algorithm/remove_if/

C++ Functors - and their uses

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM