Проверка допустимости итератора

есть ли способ проверить, является ли итератор (будь то из вектора, списка, deque...) (все-таки) уникальным, т. е. не была признана недействительной?

Я использую try -catch, но есть ли более прямой способ сделать это?

пример: (который не работает)

list<int> l;
for (i = 1; i<10; i++) {
    l.push_back(i * 10);
}

itd = l.begin();
itd++;
if (something) {
    l.erase(itd);
}

/* now, in other place.. check if itd points to somewhere meaningful */
if (itd != l.end())
{
    //  blablabla
}

11 ответов


Я предполагаю, что вы имеете в виду" является ли итератор допустимым", что он не был признан недействительным из-за изменений в контейнере (например, вставка/стирание в/из вектора). В этом случае, нет, вы не можете определить, если итератор (безопасно) уникальным.


Как сказал jdehaan, если итератор не был признан недействительным и указывает на контейнер, вы можете проверить, сравнив его с container.end().

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

std::vector<int>::iterator iter = vec.begin();
vec.resize(vec.capacity() + 1);
// iter is now singular, you may only perform assignment on it,
// there is no way in general to determine whether it is singular or not

обычно вы тестируете его, проверяя, отличается ли он от end(), например

if (it != container.end())
{
   // then dereference
}

кроме того, использование обработки исключений для замены логики плохо с точки зрения дизайна и производительности. Ваш вопрос очень хорош, и он определенно стоит замены в вашем коде. Обработка исключений, как говорится в именах, должна использоваться только для редких неожиданных проблем.


непереносимый ответ: Да-в Visual Studio

итераторы STL Visual Studio имеют режим "отладки", который делает именно это. Вы не хотите включать это в сборках кораблей (есть накладные расходы), но полезно в проверенных сборках.

читайте об этом на VC10 здесь (эта система может и на самом деле меняет каждый выпуск, поэтому найдите документы, специфичные для вашей версии).

редактировать кроме того, я должен добавить: отладочные итераторы в visual studio предназначены для немедленного взрыва при их использовании (вместо неопределенного поведения); не разрешать "запрос" их состояния.


есть ли способ проверить, является ли итератор (будь то из вектора, списка, deque...) (все-таки) уникальным, я.е не была признана недействительной ?

нет, нет. Вместо этого вам нужно контролировать доступ к контейнеру, пока существует ваш итератор, например:

  • ваш поток не должен изменять контейнер (недействительный итератор), пока он все еще использует экземпляр итератора для этого контейнер

  • Если есть риск, что другие потоки могут изменить контейнер во время итерации вашего потока, то для того, чтобы сделать этот сценарий потокобезопасным, ваш поток должен получить какую-то блокировку контейнера (чтобы он не позволял другим потокам изменять контейнер во время использования итератора)

обходные пути, такие как перехват исключения, не будут работать.

Это конкретный экземпляр более общего проблема: "могу ли я проверить/определить, является ли указатель допустимым?", ответ на который обычно " нет, вы не можете проверить его: вместо этого вы должны управлять всеми выделениями памяти и удалениями, чтобы знаю является ли какой-либо данный указатель по-прежнему действительным".


пытаться и ловить небезопасно, вы не будете или, по крайней мере, редко бросать, если ваш итератор "за пределами".

Что говорит alemjerus, итератор всегда может быть разыменован. Неважно, что uglyness лежит под ним. Вполне возможно перебирать другие области памяти и записывать в другие области, которые могут содержать другие объекты. Я смотрел на код, наблюдая, как переменные меняются без особой причины. Это ошибка, которую очень трудно обнаружить.

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

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


есть ли способ проверить, разыменован ли итератор

да, с gcc отладки контейнеров доступно как расширения GNU. Для std::list можно использовать __gnu_debug::list вместо. Следующий код будет прерван, как только будет предпринята попытка использовать недопустимый итератор. Поскольку контейнеры отладки накладывают дополнительные накладные расходы, они предназначены только при отладке.

#include <debug/list>

int main() {
  __gnu_debug::list<int> l;
  for (int i = 1; i < 10; i++) {
    l.push_back(i * 10);
  }

  auto itd = l.begin();
  itd++;
  l.erase(itd);

  /* now, in other place.. check if itd points to somewhere meaningful */
  if (itd != l.end()) {
    //  blablabla
  }
}

$ ./a.out 
/usr/include/c++/7/debug/safe_iterator.h:552:
Error: attempt to compare a singular iterator to a past-the-end iterator.

Objects involved in the operation:
    iterator "lhs" @ 0x0x7ffda4c57fc0 {
      type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
      state = singular;
      references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0
    }
    iterator "rhs" @ 0x0x7ffda4c580c0 {
      type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
      state = past-the-end;
      references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0
    }
Aborted (core dumped)

if (iterator != container.end()) {
   iterator is dereferencable !
}

Если ваш итератор не равен container.end(), и не является уникальным, Вы делаете что-то неправильно.


в некоторых контейнерах STL текущий итератор становится недопустимым при удалении текущего значения итератора. Это происходит потому, что операция стирания изменяет структуру внутренней памяти контейнера и оператор инкремента на существующий итератор указывает на неопределенное мест.

когда вы делаете следующее, итератор incementented прежде чем он передан для того чтобы стереть функцию.

if (something) l.erase(itd++);


тип параметров функции стирания любого контейнера std (как вы указали в своем вопросе, т. е. является ли он из вектора, списка, deque...) is всегда итератор контейнера только.

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

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

это означает, что итератор, указывающий на некоторый элемент, который был удален из контейнера, нельзя сравнить с контейнером.конец.)( Этот итератор недопустим, и поэтому он не разыменуется, т. е. вы не можете использовать ни операторы*, ни ->, он также не инкрементируется, т. е. вы не можете использовать оператор++, и он также не decrementable, т. е. вы не можете использовать оператор--.

это также не сопоставимо!!! Т. е. вы даже не можете использовать ни == ни != операторы

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

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

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

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

Я сделал сам функцию, которая проверяет, проверяет, знает и возвращает true, является ли данный итератор недопустимым или нет. Вы можете использовать функцию memcpy для получения состояния любого объекта, элемента, структуры, класса и т. д., И, конечно, мы всегда используем функцию memset сначала, чтобы очистить или очистить новый буфер, структуру, класс или любой объект или элемент:

bool IsNull(list<int>::iterator& i) //In your example, you have used list<int>, but if your container is not list, then you have to change this parameter to the type of the container you are using, if it is either a vector or deque, and also the type of the element inside the container if necessary.
{
    byte buffer[sizeof(i)];
    memset(buffer, 0, sizeof(i));
    memcpy(buffer, &i, sizeof(i));
    return *buffer == 0; //I found that the size of any iterator is 12 bytes long. I also found that if the first byte of the iterator that I copy to the buffer is zero, then the iterator is invalid. Otherwise it is valid. I like to call invalid iterators also as "null iterators".
}

Я уже протестировал эту функцию, прежде чем я разместил ее там и обнаружил, что эта функция работает на меня.

Я очень надеюсь, что я полностью ответил на ваш вопрос, а также очень сильно помогли вы!


используйте erase с шагом:

   if (something) l.erase(itd++);

таким образом, вы можете проверить правильность итератора.