crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

3 страниц V   1 2 3 >  
Ответить в данную темуНачать новую тему
> Удаление элементов из списка std::list в цикле
AD
  опции профиля:
сообщение 18.10.2012, 15:39
Сообщение #1


Профессионал
*****

Группа: Участник
Сообщений: 2003
Регистрация: 4.2.2008
Из: S-Petersburg
Пользователь №: 84

Спасибо сказали: 70 раз(а)




Репутация:   17  


Вот тестовый код:
#include <list>
#include <algorithm>
#include <iostream>
#include <iterator>

using namespace std;

int main()
{
    std::list<int> my_list;
    my_list.push_back(3);
    my_list.push_back(2);
    my_list.push_back(3);
    my_list.push_back(4);
    my_list.push_back(5);
    my_list.push_back(3);
    my_list.push_back(7);

    int n = 0;

    cout << "Before removing:\n";
    for(list<int>::iterator i=my_list.begin(); i!=my_list.end(); ++i, ++n)
    {
        cout << "list[" << n + 1 << "] " << *i << endl;
    }

    int j = 0;
    for(list<int>::reverse_iterator i=my_list.rbegin(); i!=my_list.rend();)
    {
        if(*i == 3)
        {
            cout << "iteration " << j + 1 << endl;
            ++i;
            my_list.erase(i.base());
            ++j;
        }
        else
            ++i;
    }

    cout << "After removing:\n";
    n = 0;
    for(list<int>::iterator i=my_list.begin(); i!=my_list.end(); ++i, ++n)
    {
        cout << "list[" << n + 1 << "] " << *i << endl;
    }
}

Интересен он следующим. Если заменить первый элемент на неудаляемый (например push_back(1)), то программа нормально выполняется. Если же тестировать указанную версию, то варианты - разные. На моей машине с Ubuntu g++ 4.6.1 он падает. На машине другого форумчанина он тоже падает, как в MSVC, так и в g++ 4.7.1, а вот у третьего форумчанина с g++ 4.6.3 этот код не падает. Что же это? Сразу говорю, что берется старый стандарт, т.е. не 2011. Понятно, что невалидный итератор, но почему же тогда иногда работает?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
D_K
  опции профиля:
сообщение 18.10.2012, 15:43
Сообщение #2


Студент
*

Группа: Участник
Сообщений: 20
Регистрация: 20.5.2009
Пользователь №: 761

Спасибо сказали: 3 раз(а)




Репутация:   1  


Должно работать.
Работает не только у меня, но и у ideone.com: http://ideone.com/8kpis
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
AD
  опции профиля:
сообщение 18.10.2012, 15:51
Сообщение #3


Профессионал
*****

Группа: Участник
Сообщений: 2003
Регистрация: 4.2.2008
Из: S-Petersburg
Пользователь №: 84

Спасибо сказали: 70 раз(а)




Репутация:   17  


Цитата(D_K @ 18.10.2012, 16:43) *
Должно работать.

Должно, но не работает! :lol:

Мда... Опишу вариант, в котором оно будет работать:

#include <list>
#include <algorithm>
#include <iostream>
#include <iterator>

using namespace std;

int main()
{
    list<int> my_list;
    my_list.push_back(3);
    my_list.push_back(2);
    my_list.push_back(3);
    my_list.push_back(4);
    my_list.push_back(5);
    my_list.push_back(3);
    my_list.push_back(7);

    int n = 0;

    cout << "Before removing:\n";
    for(list<int>::iterator i=my_list.begin(); i!=my_list.end(); ++i, ++n)
    {
        cout << "list[" << n + 1 << "] " << *i << endl;
    }

    list<int>::reverse_iterator r_end(my_list.rend());
    int j = 0;
    for(list<int>::reverse_iterator i=my_list.rbegin(); i!=r_end;)
    {
        if(*i == 3)
        {
            cout << "iteration " << j + 1 << endl;
            ++i;
            my_list.erase(i.base());
            ++j;
        }
        else
            ++i;
    }

    cout << "After removing:\n";
    n = 0;
    for(list<int>::iterator i=my_list.begin(); i!=my_list.end(); ++i, ++n)
    {
        cout << "list[" << n + 1 << "] " << *i << endl;
    }
}

Разница в коде лишь в том, что итератор окончания вынесен за цикл:
list<int>::reverse_iterator r_end(my_list.rend());
//....
    for(list<int>::reverse_iterator i=my_list.rbegin(); i!=r_end;)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
D_K
  опции профиля:
сообщение 18.10.2012, 16:02
Сообщение #4


Студент
*

Группа: Участник
Сообщений: 20
Регистрация: 20.5.2009
Пользователь №: 761

Спасибо сказали: 3 раз(а)




Репутация:   1  


Ну вот.
Видимо дело в том, что при удалении первого элемента меняется мнимый элемент "перед первым", т.е. rend().
Допустим мы указываем reverse_iterator'ом на элемент перед rend(). Далее мы инкрементируем итератор(переходим на rend()). Вызываем base() - получаем итератор на первый элемент, удаляем его, после чего rend() в списке начинает ссылаться на что-то другое, чем ссылался ранее, но наш-то итератор продолжает указывать на предыдущий rend...
Теперь надо выяснить, регламентирует ли это стандарт.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Влад
  опции профиля:
сообщение 18.10.2012, 16:03
Сообщение #5


Участник
**

Группа: Участник
Сообщений: 146
Регистрация: 20.3.2009
Из: Санкт-Петербург
Пользователь №: 627

Спасибо сказали: 46 раз(а)




Репутация:   8  


Код из сообщения #3 успешно отрабатывает в GCC 4.7.1 и валится в MSVC++ 2010 (конкретно, валится тут: for(list<int>::reverse_iterator i=my_list.rbegin(); i!=r_end; ) на вызове operator != ), - причем, валится после первой же итерации.....

Сообщение отредактировал Влад - 18.10.2012, 16:05
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
D_K
  опции профиля:
сообщение 18.10.2012, 16:21
Сообщение #6


Студент
*

Группа: Участник
Сообщений: 20
Регистрация: 20.5.2009
Пользователь №: 761

Спасибо сказали: 3 раз(а)




Репутация:   1  


На самом деле через reverse_iterator'ы нельзя удалять таким способом, т.к. по стандарту они работают с обычным итератором, который указывает на предыдущий(с точки зрения revers'а) элемент. А мы как раз его и удаляем. Соответственно reverse_iterator содержит ссылку на невалидный итератор.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Влад
  опции профиля:
сообщение 18.10.2012, 17:07
Сообщение #7


Участник
**

Группа: Участник
Сообщений: 146
Регистрация: 20.3.2009
Из: Санкт-Петербург
Пользователь №: 627

Спасибо сказали: 46 раз(а)




Репутация:   8  


Гм. Не подкинешь ссылочку на соответствующий пункт? Потому что я пока не нашел.... :-(
У меня пока складывается впечатление, что это конкретная реализация содержит "просто итератор", который указывает на предыдущий (с точки зрения revers'а) элемент, и вот этот-то просто итератор - да, становится невалидным. Поэтому и падает.

Интересно, что у Мейерса в Совете 28 приведен именно такой способ удаления элемента - с использованием (++ri).base().
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
D_K
  опции профиля:
сообщение 18.10.2012, 17:20
Сообщение #8


Студент
*

Группа: Участник
Сообщений: 20
Регистрация: 20.5.2009
Пользователь №: 761

Спасибо сказали: 3 раз(а)




Репутация:   1  


Я исхожу из описания std::reverse_iterator(24.4.1 в старом стандарте), т.к. в описании std::list явно указано typedef std::reverse_iterator<iterator> reverse_iterator


Цитата(Влад @ 18.10.2012, 18:07) *
Интересно, что у Мейерса в Совете 28 приведен именно такой способ удаления элемента - с использованием (++ri).base().

Ну если ты потом не используешь ri, то все хорошо :)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 18.10.2012, 20:53
Сообщение #9


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


юзать i = my_list.erase(i) или my_list.erase(i++).
прочие варианты будут давать неверные указатели.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
D_K
  опции профиля:
сообщение 19.10.2012, 7:46
Сообщение #10


Студент
*

Группа: Участник
Сообщений: 20
Регистрация: 20.5.2009
Пользователь №: 761

Спасибо сказали: 3 раз(а)




Репутация:   1  


Ну почему же. Должно работать и так:
 
...
   for(list<int>::reverse_iterator i=my_list.rbegin(); i!=my_list.rend();)
    {
        if(*i == 3)
        {
            cout << "iteration " << j + 1 << endl;
            list<int>::reverse_iterator rm = i;
            my_list.erase((++rm).base());
            ++i;
            ++j;
        }
        else
            ++i;
    }
...
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

3 страниц V   1 2 3 >
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 25.4.2024, 8:29