Итератор списка не может быть увеличен

У меня есть старый проект, который был построен с использованием Visual Studio 2003, и я недавно перекомпилировал его с vs2005. Однако во время выполнения я получаю следующую ошибку:

итератор списка не может быть увеличен

Я проследил программу до этой функции:

void InputQueue::update()
{
    list<PCB>::iterator iter;
    list<PCB>::iterator iterTemp;
    for(iter = begin(); iter != end(); iter++)
    {
        if (iter->arrivalTime == 0)
        {           
            ReadyQueue::getInstance()->add(*iter);
            iterTemp = iter;
            iter++;
            erase(iterTemp);
        }
    }
}

Я не специалист по C++, и это все, что мне досталось отладчику VS. Может ли кто-нибудь объяснить мне, в чем проблема?

Спасибо

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
0
16 015
8

Ответы 8

Обратите внимание, что если iter->arrivalTime == 0, то итератор списка увеличивается дважды: один раз перед удалением элемента и еще раз в конце цикла.

Если элемент, который нужно удалить, является последним элементом в списке, это, очевидно, не будет работать правильно. Осмелюсь сказать, что он никогда не работал правильно даже в VS2003, но VS2005 предупреждает вас об этом лучше. :-)

Помните, что повторение end() является неопределенным поведением. Произойти может что угодно, например, сбой программы или (в данном случае) сообщение об ошибке.

Я просто опущу несколько строк вашего кода, чтобы показать, в чем проблема:

    for(iter = begin(); iter != end(); iter++) // ***
    {
        if (iter->arrivalTime == 0)
        {                       

                iter++; // ***

        }
    }

В двух строках с пометкой *** вы увеличиваете итератор. Проблема в том, что во второй из двух строк вы не проверяете, не дошли ли вы до конца контейнера. Фактически, если вы попадаете во внутренний цикл, вы увеличиваете дважды, но проверяете только, можете ли вы увеличивать один раз.

Одно из решений - проверить, находитесь ли вы на end(), прежде чем выполнять второе приращение, но мне кажется, что вы пытаетесь выполнить ту же операцию, что и я в мой вопрос некоторое время назад, чтобы выполнить фильтрацию элементов из контейнера (карта в этом случае, но то же самое относится к большинству контейнеров STL).

Полагаю, Крис прав. Однако другая проблема может возникнуть из-за того, что вы назначаете итератору. - Гарантированно ли назначаемые итераторы списков? Не глядя на стандарт, я так не думаю, потому что в документации SGI по итераторам нигде не упоминается возможность присваивания.

Из sgi.com/tech/stl/Iterators.html кажется, что прямые итераторы назначаются. Итераторы std :: list являются двунаправленными итераторами (sgi.com/tech/stl/List.html, sgi.com/tech/stl/ReversibleContainer.html) и, таким образом, также являются итераторами прямого направления. :-)

Chris Jester-Young 13.10.2008 12:33

Хм, это то, что они подразумевают под «многопроходным»? Потому что в противном случае ничего не говорится о присваиваемости итератора (в отличие от его значения!).

Konrad Rudolph 13.10.2008 13:59

Я бы переписал ваш цикл следующим образом:

while (iter != end())
{
  if (iter->arrivalTime == 0)
  {
    ReadyQueue::getInstance()->add(*iter);
    iter = erase(iter);
  }
  else
  {
    ++iter;
  }
}

Теперь вы правильно просматриваете список, проверяя каждый индекс.

Вы не увеличиваете итератор в первой части if

1800 INFORMATION 13.10.2008 12:21

Я - iter = стереть (iter). Функция стирания возвращает новый итератор после того, который был только что удален.

Mark Ingram 13.10.2008 12:24

Да ладно, не обращай на меня внимания. Имейте в виду, что это не работает с некоторыми типами контейнеров.

1800 INFORMATION 13.10.2008 12:25

Верно, но OP упомянул, что он работал со списками (у которых есть функция стирания).

Mark Ingram 13.10.2008 13:06

Это всего лишь заметка на полях, но очень важная.

Я думаю, вы унаследовали от std::ist<PCB>. Я должен сказать: наследование для повторного использования функций не всегда хорошо для меня. Но поскольку вы также «наследуете» проект, с этим ничего не поделать ...

Наследование реализации, хотя и не идеальное, можно простить, если это только частное наследование. :-)

Chris Jester-Young 13.10.2008 12:34

Если вы получаете «несовместимый итератор списка», это, вероятно, потому, что внутри вашего «ReadyQueue :: getInstance () -> add (* iter);» вы меняете что-то в * iter, что заставляет алгоритм хеширования возвращать для стирания другое значение, чем при вставке.

Могу я предложить более простой алгоритм?

Бесплатная функция std::remove_if может использоваться для разделения вашего списка на 2 элемента, которые соответствуют или не соответствуют предикату (например, arrivalTime == 0). Он возвращает итератор, разделяющий диапазоны. Затем вы можете вызвать ReadyQueue::getInstance()->add(subrange_begin, subrange_end)(у вас же такая перегрузка, правда?) и впоследствии стереть поддиапазон.

Как раз тот случай, когда вы можете использовать алгоритмы STL вместо написания собственных циклов.

Основная причина в том, что "list.erase ()" изменит итератор. Правильная запись для цикла "for":

   for (list<CMessage*>::iterator it=que.begin(); it!=que.end(); ++it)
   {
    if (m_type == (*it)->m_type)
    {
        delete *it;
        it=que.erase(it); //"list.erase()" will change the iterator!!!
        if (it==que.end()) break; //Check again!!!
        //still has side effect here. --it?
    }
   }

Но у него все еще есть побочный эффект, поэтому решение Марка будет лучшим.

Другие вопросы по теме