Как добавлять в std::vector во время итерирования?

Имеется контейнер vector. Нужно пройтись по всем его элементам и при выполнении некоторого условия добавить или удалить элементы. При этом на каждом шаге нужно знать обо всех элементах.
Чтобы было совсем понятно, что именно я хочу получить, вот код:
for (auto obj = Organisms.begin(); obj != Organisms.end(); obj++)
	{
		(*obj)->eat();
		(*obj)->move();
		(*obj)->reproduce();
	}

В функции eat объекты могут удаляться, а в reproduce добавляться. И во всех трех функциях нужно знать обо всех существующих объектах.
  • Вопрос задан
  • 548 просмотров
Пригласить эксперта
Ответы на вопрос 4
tsarevfs
@tsarevfs Куратор тега C++
C++ developer
Возможно не самый элегантный, но достаточно предсказуемый способ -- разделить это на отдельные этапы.
for (auto obj = Organisms.begin(); obj != Organisms.end(); obj++)
{
    (*obj)->eat();
}
Organisms.erase(
    std::remove_if(Organisms.begin(), Organisms.end(),
        [](organism_ptr obj){obj->is_eaten();}
        ),
    Organisms.end()
    );
organisms_t new_organisms;
for (auto obj = Organisms.begin(); obj != Organisms.end(); obj++)
{
    organism_ptr org = (*obj)->reproduce();
    if (org)
        new_organisms.push_back(org);
}
Organisms.insert(Organisms.end(), new_organisms.begin(), new_organisms.end())
for (auto obj = Organisms.begin(); obj != Organisms.end(); obj++)
{
    (*obj)->move();
}
Ответ написан
gbg
@gbg Куратор тега C++
Любые ответы на любые вопросы
Посмотрите на семантику insert и erase - они же возвращают итератор на вновь вставленный, либо следующий за удаленным элемент. От этого и пляшите.
Ответ написан
Комментировать
Olej
@Olej
инженер, программист, преподаватель
А в чём вопрос?
(и что должно означать "нужно знать обо всех существующих объектах."?)
Некоторые ваши операции будут делать итератор obj недействительными, тогда obj++ закончится ... неизвестно чем.
В таких случаях нужно бы цепочку итераторов пробежать с начала, или от какого-то фиксированного места ранее.
Для таких вещей лучше (удобнее) переписать это всё не как цикл for, а как цикл while.
Ответ написан
vt4a2h
@vt4a2h Куратор тега C++
Senior software engineer (C++/Qt/boost)
Стоп.

Итерироваться по вектору и удалить/добавлять элементы -- плохая идея. Вектор вполне себе может реалоцироваться и всё печально закончится. Т.е. валидность итераторов не гарантируется после удаления/вставки. Можно поизвращаться конечно, но это костыльно. Либо делайте копию вектора и добавляейте/удаляйте элементы, либо выберите другой контейнер, думаю что list подойдёт или forward_list, а то и мапа какая-нибудь. Ктож знает какая у вас задача. В любом случае, я уверен что это не что-то высоконагруженное и быстрое, т.ч. преимущества вектора для последовательного перебора в виде кеш-линии вам ничего не даст.

Вот это мне не слишком понятно:
(*obj)->eat();
У вас какой-то свой хитрый итератор? Или странная логика -- кто кого съел: объект сам себя что ли? Я бы еще что-то вроде этого понял:
(*obj)->eat(*obj1);
В любом случае, советую продумать архитектуру и обратить внимания на паттерны проектирования вроде наблюдателя и посредника.

PS
И вопрос слегка не коректен, похоже что вы боретесь со следствиями какой-то архитектурной ошибки. Т.е. фактически вам нет необходимости удалять элементы из вектора при итерировании. По факту надо спросить как-то так: я решаю такую-то проблему, придумал её решить так-то, но вот незадача... как лучше сделать?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы