Я поддерживаю набор итераторов мультимножественного контейнера в отдельной структуре данных. Через некоторое время я выбираю один итератор из этой структуры данных и затем удаляю связанный элемент этого итератора из мультимножества. Я использую это как-то так:
#include <iostream>
#include <set>
int main ()
{
std::multiset<int> myints;
std::cout << "0. size: " << myints.size() << '\n';
for (int i=0; i<10; i++) myints.insert(i);
std::cout << "1. size: " << myints.size() << '\n';
myints.insert (5);
std::cout << "2. size: " << myints.size() << '\n';
std::multiset<int>::iterator it = myints.find(5);
myints.erase (it);
std::cout << "3. size: " << myints.size() << '\n';
myints.erase (it);
std::cout << "4. size: " << myints.size() << '\n';
return 0;
}
Тем не менее, оказывается, что второй myints.erase (it);
вызвать ошибку сегментации. Поэтому я перехожу на следующий код, и он работает. Мне было интересно, если это хороший путь или это выполнимо undefined
ситуация:
int main ()
{
std::multiset<int> myints;
std::cout << "0. size: " << myints.size() << '\n';
for (int i=0; i<10; i++) myints.insert(i);
std::cout << "1. size: " << myints.size() << '\n';
myints.insert (5);
std::cout << "2. size: " << myints.size() << '\n';
std::multiset<int>::iterator it = myints.find(5);
myints.erase (it);
std::cout << "3. size: " << myints.size() << '\n';
std::multiset<int>::iterator newit = myints.find(*it);
myints.erase (newit);
std::cout << "4. size: " << myints.size() << '\n';
return 0;
}
erase(it)
делает недействительным итератор it
то есть это бесполезно после erase
и что-либо делать с этим приводит к неопределенному поведению. (Вы, вероятно, ожидали, что он «перейдет к следующему элементу», когда элемент, на который он указывает, удален, но это не то, что он делает.)
Ваш второй подход не исправляет это. Это может сработать случайно, но вы все еще используете повторно it
после того, как вы стерли это.
редактировать: Учитывая ваше описание «Я хочу стереть только одну 5 из мультимножества и сохранить ее действительной после стирания для следующего стирания.», Вы можете сделать это, создав копию итератора, увеличивая оригинал и затем стирая копию:
it = myints.find(5);
// better add a check here to make sure there actually is a 5 ...
std::multiset<int>::iterator newit = it;
it++;
myints.erase(newit);
Так как вы уже увеличили it
, он остается в силе, потому что он не указывает на элемент, который убит erase
,
Однако я, честно говоря, не могу представить ситуацию, в которой это могло бы быть полезным или, скорее, необходимым.
В вашем первом подходе итератор становится недействительным, когда вы удаляете элемент, указанный этим итератором, и позже, когда вы попытаетесь стереть снова, используя тот же итератор, вы получите ошибку сегментации.
В вашем втором подходе, так как каждый раз, когда вы делаете, находите после стирания, что дает вам правильный итератор.
Вы можете исправить первый случай, внеся следующие изменения в свой код. Оператор постинкрементного возврата вернет новый объект и переместит итератор в следующую позицию. Я также предлагаю сделать проверку конца перед удалением, иначе вы можете получить неопределенное поведение.
std::multiset<int>::iterator it = myints.find(5);
if(it != myints.end())
myints.erase (it++);
std::cout << "3. size: " << myints.size() << '\n';
if(it != myints.end())
myints.erase (it++);
std::cout << "4. size: " << myints.size() << '\n';