Подтвердить что ты не робот

Удалить элементы вектора внутри цикла

Я знаю, что есть похожие вопросы к этому, но мне не удалось найти способ моего кода. Я хочу просто удалить/удалить элемент вектора, проверив атрибут этого элемента внутри цикла. Как я могу это сделать? Я пробовал следующий код, но я получаю неопределенное сообщение об ошибке:

Функция

'operator =' недоступна в 'Player.

 for (vector<Player>::iterator it = allPlayers.begin(); it != allPlayers.end(); it++)
 {
     if(it->getpMoney()<=0) 
         it = allPlayers.erase(it);
     else 
         ++it;
 }

Что мне делать?

Обновление:. Считаете ли вы, что вопрос vector:: erase с элементом указателя относится к той же проблеме? Нужен ли мне оператор присваивания? Зачем?

4b9b3361

Ответ 1

Вы не должны увеличивать it в цикле for:

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

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

Обратите внимание на прокомментированную часть; it++ не нужен, так как it увеличивается в самом теле.

Что касается ошибки, то функция "operator =" недоступна в "Player", она исходит из использования erase() которая внутренне использует operator= для перемещения элементов в векторе. Чтобы использовать erase(), объекты класса Player должны быть назначаемыми, что означает, что вам нужно реализовать operator= для класса Player.

Во всяком случае, вам следует избегать необработанного цикла 1, насколько это возможно, и вместо этого следует использовать алгоритмы. В этом случае популярный Erase-Remove Idiom может упростить то, что вы делаете.

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

1. Это один из лучших разговоров Шона Родитель, который я когда-либо наблюдал.

Ответ 2

Забудьте о цикле и используйте альгонисты диапазона std или boost.
Используя Boost.Range en Lambda, он будет выглядеть так:

boost::remove_if( allPlayers, bind(&Player::getpMoney, _1)<=0 );

Ответ 3

if(allPlayers.empty() == false) {
    for(int i = allPlayers.size() - 1; i >= 0; i--) {
        if(allPlayers.at(i).getpMoney() <= 0) {
            allPlayers.erase( allPlayers.begin() + i ); 
        }
    }
}

Это мой способ удалить элементы в векторе. Это легко понять и не нуждается в каких-либо трюках.

Ответ 4

Ваша конкретная проблема заключается в том, что ваш класс Player не имеет оператора присваивания. Вы должны сделать "Player" скопированным или подвижным, чтобы удалить его из вектора. Это связано с тем, что вектор должен быть смежным и, следовательно, необходимо изменить порядок элементов, чтобы заполнить пробелы, созданные при удалении элементов.

также:

Использовать алгоритм std

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

или даже проще, если у вас есть повышение:

boost::remove_erase_if(allPlayers, [](const Player& player)
{
    return player.getpMoney() <= 0;
});

См. ответ TimW, если у вас нет поддержки lambdas С++ 11.

Ответ 5

Или сделайте цикл назад.

for (vector<Player>::iterator it = allPlayers.end() - 1; it != allPlayers.begin() - 1; it--)
    if(it->getpMoney()<=0) 
        it = allPlayers.erase(it);

Ответ 6

С++ 11 представила новую коллекцию функций, которая будет использоваться здесь.

allPlayers.erase(
    std::remove_if(allPlayers.begin(), allPlayers.end(),
        [](auto& x) {return x->getpMoney() <= 0;} ), 
    allPlayers.end()); 

И тогда вы получаете преимущество, заключающееся в том, что вам не нужно много перемещать конечные элементы.

Ответ 7

Поздний ответ, но, увидев неэффективные варианты:

  1. std::remove или std::remove_if - путь.
  2. Если по какой-либо причине они недоступны или не могут использоваться по какой-либо другой причине, сделайте то, что они скрывают от вас.

Код для эффективного удаления элементов:

auto pos = container.begin();
for(auto i = container.begin(); i != container.end(); ++i)
{
    if(isKeepElement(*i)) // whatever condition...
    {
        *pos++ = *i; // will move, if move assignment is available...
    }
}
// well, std::remove(_if) stops here...
container.erase(pos, container.end());

Возможно, вам потребуется написать такой цикл явно, например, если вам нужен сам итератор, чтобы определить, должен ли элемент быть удален (параметр условия должен принять ссылку на элемент, помните?), Например, из-за специфической связи с преемником/предшественником (если это отношение равно равенству, то есть std::unique).