векторные итераторы несовместимы при стирании из вектора

У меня есть карта, элементы которой являются векторами. Я должен удалить из этих векторов все элементы, которые равны специальному числу num

std::map<size_t,std::vector<size_t> > myMap;
for (std::map<size_t,std::vector<size_t> >::iterator itMap = myMap.begin();itMap != myMap.end();++itMap )
{
    for (std::vector<size_t>::iterator itVec = itMap->second.begin();itVec != itMap->second.end();)
    {
        auto itNextVec = itVec;
        ++itNextVec;
        if (*itVec == num)
        {
            itMap->second.erase(itVec );
        }
        itVec = itNextVec;
    }
}

Код вызывает exepsion во время выполнения. В VS - vector iterators incompatible. Может ли кто-нибудь указать, в чем причина этого?

Спасибо


person Yakov    schedule 23.02.2013    source источник
comment
Вы знаете о std::remove, верно? Это было бы не только красивее, но и эффективнее. auto& v = itMap->second; v.erase(std::remove(v.begin(), v.end(), num), v.end());   -  person Benjamin Lindley    schedule 23.02.2013


Ответы (4)


std::vector::erase возвращает iterator в следующую позицию списка, поэтому, когда вы выполняете стирание, вы должны сделать свой итератор равным возвращаемому значению.

Единственное, что вы должны учитывать, это то, что возвращенный iterator может быть концом, поэтому вы должны проверить это.

Что мне лично нравится делать, так это то, что после выполнения стирания и получения следующей позиции итератора я возвращаюсь к предыдущей позиции возвращенного итератора, а затем вызываю продолжение на for loop

Пример:

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> myInt;
    myInt.push_back(1);myInt.push_back(2);myInt.push_back(3);

    for(auto iter = myInt.begin();
        iter != myInt.end();
        ++iter)
    {
        if(*iter == 1)
        {
            iter = myInt.erase(iter);
            if(iter != myInt.begin())
            {
                iter = std::prev(iter);
                continue;
            }
        }

        std::cout << *iter << std::endl;
    }
}

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

person Caesar    schedule 23.02.2013
comment
Можете ли вы объяснить мне, почему мой подход не очень хорош: я не ссылаюсь и не увеличиваю его после удаления, а использую auto itNextVec для его обновления. - person Yakov; 23.02.2013
comment
@Yakov: erase не только делает недействительным итератор, к которому он был вызван, но и делает недействительным каждый итератор, указывающий на позицию после него. - person Benjamin Lindley; 23.02.2013
comment
@BenjaminLindley - только в векторе или в каждом стандартном контейнере? - person Yakov; 23.02.2013
comment
@Yakov: вектор и очередь. Другие контейнеры никогда не делают недействительными свои итераторы, если конкретный объект не стерт. - person Benjamin Lindley; 23.02.2013

стирание сделает итератор недействительным

Iterator validity
Iterators, pointers and references pointing to position (or first) and beyond are 
invalidated, with all iterators, pointers and references to elements before position (or 
first) are guaranteed to keep referring to the same elements they were referring to 
before the call.
person Aditya Sihag    schedule 23.02.2013

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

Если вы проверите, например. по этой ссылке вы увидите, что функция erase возвращает итератор в следующий элемент. Продолжить петлю этой (конечно, не увеличивая ее).

person Some programmer dude    schedule 23.02.2013
comment
Но по этой причине я держу auto itNextVec - person Yakov; 23.02.2013

Рассмотрите возможность использования другого класса коллекции, отличного от vector, или создания нового вектора с удаленными нужными элементами, а не удалением из существующего вектора.

person johnbakers    schedule 23.02.2013