Сохранение итераторов std:: list допустимыми через вставку

Примечание: это не вопрос, Должен ли я "использовать List или deque". Это вопрос о валидности итераторов перед лицом insert().


это может быть простой вопрос, и я просто слишком туп, чтобы увидеть правильный способ сделать это. Я реализую (к лучшему или худшему) буфер сетевого трафика как std::list<char> buf, и я сохраняю свою текущую позицию чтения в качестве итератора readpos.

когда я добавляю данные, я что-то делаю как

buf.insert(buf.end(), newdata.begin(), newdata.end());

мой вопрос теперь, как мне сохранить readpos итератор действует? Если он указывает на середину старого buf, тогда все должно быть хорошо (по гарантиям итератора для std:: list), но обычно я могу читать и обрабатывать все данные, и у меня есть readpos == buf.end(). После вставки, я хочу!--3--> всегда указать на следующий непрочитанный символ, который в случае вставки должен быть первым вставлен один.

какие предложения? (Коротко изменение буфера на std::deque<char>, который, как представляется, гораздо лучше подходит для этой задачи, как предлагается ниже.)

обновление: из быстрого теста с GCC4.4 я замечаю, что дек и лист ведут себя по-разному в отношении readpos = buf.end(): после вставки в конце readpos разбивается в списке, но указывает на следующий элемент в deque. это стандартная гарантия?

(по cplusplus, любой deque:: вставить() недействительным все итераторы. Это нехорошо. Может быть, использование счетчика лучше, чем итератор для отслеживания позиции в deque?)

4 ответов


if (readpos == buf.begin())
{
    buf.insert(buf.end(), newdata.begin(), newdata.end());
    readpos = buf.begin();
}
else
{
    --readpos;
    buf.insert(buf.end(), newdata.begin(), newdata.end());
    ++readpos;
}

Не элегантный, но он должен работать.


от http://www.sgi.com/tech/stl/List.html

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

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

однако...

std::list< char > - очень неэффективный способ решения этой проблемы. Каждый байт, который вы храните в std::list требуется указатель для отслеживания байта, плюс размер структуры узла списка, обычно еще два указателя. Это не менее 12 или 24 байт (32 или 64-бит) памяти, используемой для отслеживания одного байта данных.

std::deque< char> вероятно, лучший контейнер для этого. Как std::vector он обеспечивает вставки постоянного времени на задней части однако он также обеспечивает удаление постоянного времени на фронте. Наконец, как std::vector std::deque контейнер произвольного доступа поэтому вы можете используйте смещения / индексы вместо итераторов. Эти три особенности делают его эффективным выбором.


Я действительно был тупым. Стандарт дает нам все необходимые инструменты. В частности, требования к контейнеру последовательности 23.2.3 / 9 гласят:

iterator, возвращенный из a.insert(p, i, j) указывает на копию первого элемента вставить в a или p Если i == j.

далее описание list::insert говорит (23.3.5.4/1):

не влияет на валидность итераторов и ссылки на литературу.

так на самом деле, если pos мой текущий итератор внутри списка, который потребляется, я могу сказать:

auto it = buf.insert(buf.end(), newdata.begin(), newdata.end());

if (pos == buf.end()) { pos = it; }

диапазон новых элементов в моем списке -[it, buf.end()), а диапазон еще необработанных элементов -[pos, buf.end()). Это работает, потому что если pos равна buf.end() до вставки, после чего его еще после вставки, после вставки не лишает любой итераторы, даже не конец.


list<char> - Это очень неэффективный способ хранения строки. Вероятно, он в 10-20 раз больше самой строки, плюс вы преследуете указатель для каждого символа...

вы рассматривали возможность использования std::dequeue<char> вместо?

[edit]

чтобы ответить на ваш фактический вопрос, добавление и удаление элементов не делает недействительными итераторы в list... Но!--3--> по-прежнему будет end(). Поэтому вам нужно будет проверить это как особый случай в укажите, куда вы вставляете новый элемент, чтобы обновить свой readpos итератор.