Удалить элементы вектора внутри цикла
Я знаю, что есть похожие вопросы к этому, но мне не удалось найти способ моего кода. Я хочу просто удалить/удалить элемент вектора, проверив атрибут этого элемента внутри цикла. Как я могу это сделать? Я пробовал следующий код, но я получаю неопределенное сообщение об ошибке:
Функция
'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 с элементом указателя относится к той же проблеме? Нужен ли мне оператор присваивания? Зачем?
Ответы
Ответ 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
Поздний ответ, но, увидев неэффективные варианты:
-
std::remove
или std::remove_if
- путь. - Если по какой-либо причине они недоступны или не могут использоваться по какой-либо другой причине, сделайте то, что они скрывают от вас.
Код для эффективного удаления элементов:
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
).