Делает ли std::list::clear недействительным итератор std::list::end?

Проверьте этот код:

#include "stdafx.h"
#include <list>

int _tmain(int argc, _TCHAR* argv[])
{
    std::list<int> mylist;
    mylist.push_back(1);
    std::list<int>::iterator i = mylist.end();
    if( i == mylist.end() )
        printf( "end is end\n" );

    mylist.clear();
    if( i == mylist.end() )
        printf( "never get here because Microsoft seems to "
                "think the iterator is no longer safe.\n" );

    return 0;
}

Теперь, согласно cplusplus.com, это не должно быть проблемой, и в режиме выпуска, я думаю, это нормально и на самом деле не вызывает никаких проблем, но отладка становится невозможной, так как это просто не позволяет мне продолжить. Любые указатели?


person Richard Fabian    schedule 01.02.2013    source источник
comment
Интересно. Я думал, что .end тоже недействителен, но в статье написано обратное. Интересно, что ideone.com/Y338N8 выполняет его, как и ожидалось. +1   -  person Kiril Kirov    schedule 01.02.2013
comment
Это вполне логично, поскольку код, который я пытаюсь перенести на Windows, был написан для MacOS и Linux. Оба работают под управлением GCC, как и ideone. Я предполагаю, что это ошибка библиотеки.   -  person Richard Fabian    schedule 02.02.2013


Ответы (3)


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

C++11 23.3.5.4/3 Эффекты: делает недействительными только итераторы и ссылки на стертые элементы.

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

person Mike Seymour    schedule 01.02.2013
comment
+1, удалил мой ответ. Хотя я припоминаю из предыдущих разговоров, что использование динамически выделяемого часового явно поддерживается стандартом, это не означает, что clear() нужно его освобождать. Только что дважды проверил нашу реализацию, и clear() реализован как erase(begin(),end()) и не делает недействительными итераторы end(). Об этом следует сообщить в Microsoft как об ошибке в их реализации. - person David Rodríguez - dribeas; 01.02.2013
comment
Спасибо, Майк и @DavidRodríguez-dribeas. Да, это ошибка в реализации Visual C++ 2012 std::list. Это отступление от Visual C++ 2010. Обратите внимание, что erase так же не работает. Я проверил исправление этой ошибки несколько месяцев назад, и это исправление появится в следующей версии стандартной библиотеки Visual C++. Исправление исправляет как erase, так и clear. (Об этой ошибке сообщил клиент, но не через Microsoft Connect, поэтому у меня нет ссылки, на которую я мог бы вас направить.) - person James McNellis; 02.02.2013
comment
Сообщено в Microsoft, исправлено, но только в VS2013: connect.microsoft.com/VisualStudio/feedback/details/808659/ - person Macker; 06.03.2014

Из С++ 11, таблица 100 (требования к контейнеру последовательности):

clear() [...] может сделать недействительным итератор за концом.

И std::list, конечно же, является шаблоном контейнера последовательности (23.3.5.1/2):

Список удовлетворяет всем требованиям к контейнеру, реверсивному контейнеру (приведенному в двух таблицах в 23.2), контейнеру последовательности, включая большинство необязательных требований к контейнеру последовательности (23.2.3), и контейнера с поддержкой распределителя (таблица 99). Исключением являются оператор[] и функции-члены at, которые не предоставляются. Здесь приводятся описания только для операций со списком, которые не описаны ни в одной из этих таблиц или для операций, для которых имеется дополнительная семантическая информация.

person Kerrek SB    schedule 01.02.2013
comment
Чтобы добавить к этому: итератор end является сингулярным значением, и большинство базовых контейнеров узлов, которые гарантируют стабильность итераторов, дают эту гарантию только для несингулярных значений. - person Matthieu M.; 01.02.2013
comment
Я считаю, что это следует читать, поскольку есть контейнеры, для которых он делает недействительными итераторы, прошедшие конец, и контейнеры, для которых это не так. В противном случае это будет конфликтующая спецификация. - person Andy Prowl; 01.02.2013
comment
Но специфичная для списка спецификация 23.3.5.4/3 говорит, что делает недействительными только итераторы и ссылки на стертые элементы. - person Mike Seymour; 01.02.2013
comment
@MikeSeymour прав. Общие требования в таблице 100 применяются только в том случае, если в конкретных требованиях к контейнеру не указано иное. В данном конкретном случае требования к std::list<>::clear() явно дают более строгие гарантии. - person David Rodríguez - dribeas; 01.02.2013

Это на самом деле недействительно. Итераторы действительны только для контейнера текущего состояния. Как только вы добавляете или удаляете элементы, итератор становится недействительным.

В статье, на которую вы ссылаетесь, не говорится, что то, что вы делаете, действительно. Они получают новый итератор после очистки.

Причина, по которой он не отображается в коде выпуска, заключается в том, что отладка, которая выявляет проблему, отключена.

person Andrew Brock    schedule 01.02.2013
comment
И еще раз: по ссылке в вопросе: Все итераторы, ссылки и указатели, относящиеся к этому контейнеру, недействительны, кроме конечных итераторов. - person Kiril Kirov; 01.02.2013
comment
@meh: Нет, вы ошибаетесь, конечный итератор может быть признан недействительным - person David Rodríguez - dribeas; 01.02.2013
comment
Как только вы добавляете или удаляете элементы, итератор становится недействительным. В общем случае это не так; каждый контейнер имеет свои собственные правила аннулирования, и итераторы list остаются действительными до тех пор, пока элемент, на который они ссылаются, все еще находится в списке. - person Mike Seymour; 01.02.2013
comment
@DavidRodríguez-dribeas - я ничего не говорю, я просто скопировал цитату из статьи, на которую есть ссылка в вопросе :) Также +1 за мнение Майка - person Kiril Kirov; 01.02.2013