0

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);
    }    
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I think you mean `enemies.erase(enemies.begin() + i);` – DimChtz Mar 28 '16 at 22:58
  • @DimChtz Lol never mind. I had tried that before and the reason it wasn't working is because i declared the active variable but never initialized it. my bad but thanks. – RoundSquare Mar 28 '16 at 23:04

4 Answers4

1

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, i.e. 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()); 
        */
    }
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
1

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();
paddy
  • 60,864
  • 6
  • 61
  • 103
  • Note that this code is for C++11 onwards. Shouldn't really have to state that these days, but some people are still using old compilers. Also, the fact I'm passing `const Enemy *` mandates that you implemented `Enemy::IsActive` with const-correctness. If you have compile errors, consider that. – paddy Mar 28 '16 at 23:19
0

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);
Tomáš Šíma
  • 834
  • 7
  • 26
0

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

Community
  • 1
  • 1
shawn1874
  • 1,288
  • 1
  • 10
  • 26