Как вызвать стирание с обратным итератором - PullRequest
153 голосов
/ 02 декабря 2009

Я пытаюсь сделать что-то вроде этого:

for ( std::list< Cursor::Enum >::reverse_iterator i = m_CursorStack.rbegin(); i != m_CursorStack.rend(); ++i )
{
    if ( *i == pCursor )
    {
        m_CursorStack.erase( i );
        break;
    }
}

Однако стирание требует итератор, а не обратный итератор. Есть ли способ преобразовать обратный итератор в обычный итератор или другой способ удалить этот элемент из списка?

Ответы [ 10 ]

154 голосов
/ 02 декабря 2009

После дополнительных исследований и испытаний я нашел решение. По-видимому, согласно стандарту [24.4.1 / 1] соотношение между i.base () и i:

&*(reverse_iterator(i)) == &*(i - 1)

(из статьи доктора Доббса ):

alt text

Так что вам нужно применить смещение при получении базы (). Поэтому решение:

m_CursorStack.erase( --(i.base()) );

EDIT

Обновление для C ++ 11.

reverse_iterator i без изменений:

m_CursorStack.erase( std::next(i).base() );

reverse_iterator i продвинутый:

std::advance(i, 1);
m_CursorStack.erase( i.base() );

Я нахожу это намного яснее, чем мое предыдущее решение. Используйте то, что вам нужно.

14 голосов
/ 28 сентября 2010

Обратите внимание, что m_CursorStack.erase( (++i).base()) может быть проблемой, если используется в цикле for (см. Оригинальный вопрос), потому что это изменяет значение i. Правильное выражение m_CursorStack.erase((i+1).base())

11 голосов
/ 10 декабря 2012

... или другой способ удалить этот элемент из списка?

Для этого требуется флаг -std=c++11 (для auto):

auto it=vt.end();
while (it>vt.begin())
{
    it--;
    if (*it == pCursor) //{ delete *it;
        it = vt.erase(it); //}
}
3 голосов
/ 18 октября 2011
typedef std::map<size_t, some_class*> TMap;
TMap Map;
.......

for( TMap::const_reverse_iterator It = Map.rbegin(), end = Map.rend(); It != end; It++ )
{
    TMap::const_iterator Obsolete = It.base();   // conversion into const_iterator
    It++;
    Map.erase( Obsolete );
    It--;
}
3 голосов
/ 02 декабря 2009

При использовании метода reverse_iterator base() и уменьшении результата здесь работает, стоит отметить, что reverse_iterator s не присвоен тот же статус, что и обычным iterator s. В общем, вы должны предпочитать обычные iterator с reverse_iterator с (а также const_iterator с и const_reverse_iterator с), именно по этим причинам. См. Журнал Доктора Доббса для подробного обсуждения причин.

2 голосов
/ 11 мая 2018

Забавно, что на этой странице пока нет правильного решения. Таким образом, следующее является правильным:

В случае прямого итератора решение является прямым:

std::list< int >::iterator i = myList.begin();
while ( ; i != myList.end(); ) {
  if ( *i == to_delete ) {
    i = myList.erase( i );
  } else {
    ++i;
  } 
}

В случае обратного итератора вам нужно сделать то же самое:

std::list< int >::reverse_iterator i = myList.rbegin();
while ( ; i != myList.rend(); ) {
  if ( *i == to_delete ) {
    i = decltype(i)(myList.erase( std::next(i).base() ));
  } else {
    ++i;
  } 
}

Примечания:

  • Вы можете построить reverse_iterator из итератора
  • Вы можете использовать возвращаемое значение std::list::erase
2 голосов
/ 07 февраля 2018

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

std::set<int> set{1,2,3,4,5};

for (auto itr = set.rbegin(); itr != set.rend(); )
{    
    if (*itr == 3)
    {
        auto it = set.erase(--itr.base());
        itr = std::reverse_iterator(it);            
    }
    else
        ++itr;
}
2 голосов
/ 06 сентября 2011

Если вам не нужно стирать все по ходу дела, то для решения проблемы вы можете использовать идиому удаления-удаления:

m_CursorStack.erase(std::remove(m_CursorStack.begin(), m_CursorStack.end(), pCursor), m_CursorStack.end());

std::remove меняет все элементы в контейнере, которые соответствуют pCursor, до конца и возвращает итератор к первому элементу соответствия. Затем erase, использующий диапазон, сотрет с первого совпадения и перейдет к концу. Порядок несовпадающих элементов сохраняется.

Это может сработать быстрее для вас, если вы используете std::vector, где удаление в середине содержимого может потребовать большого количества копирования или перемещения.

Или, конечно, ответы выше, объясняющие использование reverse_iterator::base(), интересны и стоит знать, чтобы решить поставленную задачу, я бы сказал, что std::remove лучше подходит.

0 голосов
/ 22 февраля 2019

В дополнение к ответам других и потому, что я наткнулся на этот вопрос во время поиска std :: string без особого успеха, здесь идет ответ с использованием std :: string, std :: string :: erase и std :: reverse_iterator

Моя проблема заключалась в удалении имени файла изображения из полной строки имени файла. Первоначально она была решена с помощью std :: string :: find_last_of, но я исследую альтернативный способ с помощью std :: reverse_iterator.

std::string haystack("\\\\UNC\\complete\\file\\path.exe");
auto&& it = std::find_if( std::rbegin(haystack), std::rend(haystack), []( char ch){ return ch == '\\'; } );
auto&& it2 = std::string::iterator( std::begin( haystack ) + std::distance(it, std::rend(haystack)) );
haystack.erase(it2, std::end(haystack));
std::cout << haystack;  ////// prints: '\\UNC\complete\file\'

Используются алгоритм, итератор и заголовки строк.

0 голосов
/ 21 сентября 2012

Просто хотел кое-что прояснить: в некоторых из приведенных выше комментариев и ответов портативная версия для стирания упоминается как (++ i) .base (). Однако, если я что-то упускаю, правильное утверждение (++ ri) .base () означает, что вы «увеличиваете» обратный итератор (не итератор).

Я столкнулся с необходимостью сделать что-то подобное вчера, и этот пост был полезен. Спасибо всем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...