Как выполнить итерацию назад по списку STL?

Я пишу кросс-платформенный код между Windows и Mac.

Если list :: end () "возвращает итератор, который обращается к местоположению, следующему за последним элементом в списке" и может быть проверен при перемещении по списку вперед, каков наилучший способ перемещения назад?

Этот код работает на Mac, но не на Windows (не может уменьшаться после первого элемента):

list<DVFGfxObj*>::iterator iter = m_Objs.end();
for (iter--; iter!=m_Objs.end(); iter--)// By accident discovered that the iterator is circular ?
{
}

это работает в Windows:

list<DVFGfxObj*>::iterator iter = m_Objs.end();
    do{
        iter--;
    } while (*iter != *m_Objs.begin());

Есть ли другой способ вернуться назад, который можно было бы реализовать в цикле for?

Будет только случайностью реализации, что ваш первый пример (круговой итератор, сравнение с end ()) будет работать.

Justsalt 10.10.2008 00:23
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
36
1
51 142
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Используйте reverse_iterator вместо iterator. Используйте rbegin() и rend() вместо begin() и end().

Другая возможность, если вам нравится использовать макрос BOOST_FOREACH, - это использовать макрос BOOST_REVERSE_FOREACH, представленный в Boost 1.36.0.

Документация для итератора и reverse_iterator почти одинакова. итератор двунаправленный, так в чем разница?

AlanKley 10.10.2008 00:00

Разница в том, что вы по-прежнему используете «++ Iter» для увеличения итератора, а не «--Iter». Или я не прав?

steffenj 10.10.2008 00:03

Нет, вы правы, что немного странно увеличивать, чтобы вернуться назад, но также имеет смысл. Хотя reverse_iterator кажется ненужным, учитывая, что итератор двунаправлен. В документации для reverse_iterator говорится, что он действует в обратном списке; конечно, он не меняет внутренний список сначала.

AlanKley 10.10.2008 00:08

@AlanKey: если вы знаете, что имеете дело со списком, вы можете просто уменьшить обычный итератор. Обратные итераторы становятся самостоятельными, когда вы пишете общий код - им не нужно делать ничего особенного, чтобы просмотреть коллекцию в обратном направлении - им просто нужно дать обратные итераторы

Michael Burr 10.10.2008 01:56

Да, Майк, моим первым подходом было уменьшение обычного итератора, как показано в двух моих примерах. Используя этот метод, я не был уверен, как лучше всего проверить начало списка. Поведение в Windows и Mac оказалось разным.

AlanKley 10.10.2008 06:10

Не все «прямые» итераторы «двунаправленные». Это зависит от класса коллекции.

Jesse Chisholm 17.10.2013 07:05

@AlanKey: Основное различие в том, что означают конечные точки. iterator :: begin () ссылается на первый элемент, iterator :: end () ссылается на один за последним элементом, reverse_iterator :: rbegin () ссылается на последний элемент, reverse_iterator :: rend () ссылается на один перед первым элементом. При итерации с помощью цикла for удобно иметь возможность увеличивать на единицу после конца коллекции в любом направлении, в котором вы выполняете итерацию, что упрощает использование reverse_iterator, чем перемещение назад через двунаправленный итератор.

pavon 07.05.2014 20:35

Вероятно, вам нужны обратные итераторы. Из памяти:

list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
for( ; iter != m_Objs.rend(); ++iter)
{
}

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

AlanKley 10.10.2008 00:02

это должно быть "...> :: reverse_iterator iter = ..."

steffenj 10.10.2008 00:04

@AlanKley Я думаю, что цикл for, который вы задали в своем вопросе, в порядке. Причина, по которой я думаю, что это работает, заключается в том, что функция-член .end () возвращает контрольное значение, которое назначается как значение следующего указателя из последнего элемента, а также указатель prev на первом элементе.

Anthony Cramp 10.10.2008 00:30

К сожалению, перечитайте вопрос ... не работает в Windows. Я также тестировал код на Mac.

Anthony Cramp 10.10.2008 00:31

Как уже упоминал Ферруччо, используйте reverse_iterator:

for (std::list<int>::reverse_iterator i = s.rbegin(); i != s.rend(); ++i)

Это должно работать:

list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
for (; iter!= m_Objs.rend(); iter++)
{
}

Лучший / самый простой способ отменить итерацию списка - это (как уже говорилось) использовать обратные итераторы rbegin / rend.

Однако я хотел упомянуть, что реверсивные итераторы реализованы с сохранением «текущей» позиции итератора по очереди (по крайней мере, в реализации стандартной библиотеки GNU).

Это сделано для упрощения реализации, чтобы диапазон в обратном порядке имел ту же семантику, что и диапазон вперед [начало, конец) и [rbegin, rend)

Это означает, что разыменование итератора включает создание нового временного объекта, а затем его уменьшение, каждый раз:

  reference
  operator*() const
  {
_Iterator __tmp = current;
return *--__tmp;
  }

Таким образом, разыменование reverse_iterator медленнее обычного итератора.

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

for ( iterator current = end() ; current != begin() ; /* Do nothing */ )
{
    --current; // Unfortunately, you now need this here
    /* Do work */
    cout << *current << endl;
}

Тестирование показало, что это решение в ~ 5 раз быстрее для каждого разыменования, использованного в теле цикла.

Примечание. Тестирование не проводилось с приведенным выше кодом, так как std :: cout был бы узким местом.

Также обратите внимание: разница во времени на настенных часах составляла ~ 5 секунд при размере std :: list в 10 миллионов элементов. Таким образом, если размер ваших данных не настолько велик, просто придерживайтесь rbegin () rend ()!

Посмотрев на это еще раз, возможно, вы захотите просто инициализировать с помощью current = --end (); и оставьте шаг приращения внутри цикла for. Это также защитит от пустого массива, которого нет в моей версии выше. Я пока оставлю оригинал опубликованным, так как я не тестировал.

mmocny 23.10.2013 00:45

Я не думаю, что это сработает, если вы также не измените условие цикла. В противном случае вам будет не хватать первого элемента (потому что цикл не будет выполняться, если current == begin())

Griddo 21.07.2014 15:53

Первая строка цикла for выполняет декремент, так что да, я думаю. Просто напишите быстрый тест, чтобы попробовать! В любом случае, я больше не думаю, что это лучший способ использования этой идиомы, и некоторые недавние тесты, которые я проводил, больше не показывают заметного улучшения скорости обратного обращения итераторов при полной оптимизации. Тем не менее, стоит все же отметить, что происходит под капотом, и провести соответствующее тестирование!

mmocny 21.07.2014 17:15

Я намеревался прокомментировать ваш комментарий, а не ответ, но SO удалил @ -part. Код в вашем ответе работает отлично, однако я согласен, что он может быть не лучшим;) Обновлено: quick тестовое задание

Griddo 22.07.2014 09:56

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