Это плохая идея сделать копию итератора? - PullRequest
0 голосов
/ 03 мая 2018

Я думаю написать некоторый код, который будет сводиться к этому. T - это тип коллекции (в настоящее время std::map, если это имеет значение).

T coll;

// ...

T::iterator it, prev;

prev = coll.end();

for(it = coll.begin(); it != coll.end(); ++it) {
    if(prev != coll.end())
        { do something involving previous element; }

    do something involving this element;

    prev = it;
}

У меня такой вопрос, это плохая идея - скопировать it в prev вот так? Плохой стиль? Скорее всего, где-то раздражать педанта? Можно разбить в зависимости от тонких деталей типа T?

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

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


Добавление:

Другая причина, по которой это возникает, заключается в том, что мой настоящий код не будет включать цикл for, который я написал выше. На самом деле у меня есть управляемый событиями код; который будет выглядеть примерно так:

void onStartDoingSomething() {
    it = coll.start();
    prev = coll.end();
    startOperation(it, prev);
    timerStart();
}

void onTimer() {
    if(finished with operation on it) {
        prev = it;
        ++it;
        startOperation(it, prev);
        if(it == coll.end() {
            timerStop();
            call operation finished;
        }
    }
}

void startOperation(T::iterator next, T::iterator prev) {
    if(prev != coll.end()) {
        finish operation on prev;
    }

    if(next != coll.end()) {
        start operation on next;
    }
}

Таким образом, другой способ сформулировать мой вопрос может быть следующим: «Нужно ли использовать итераторы и методы begin() и end() класса коллекции, только в обычных циклах for со смещением, или вы можете использовать их произвольно? Есть ли какое-либо состояние в цикле for или все состояние в итераторе? " Теперь я знаю, что нет ничего похожего на «состояние, хранимое в цикле for», и, насколько я знаю, для итератора крайне важно содержать все необходимое ему состояние. Так что, если на самом деле итератор не только содержит все необходимое ему состояние, но и на 100% безопасно копируется, тогда ответ на мой оригинальный вопрос «Нет, это неплохая идея». Но скрытая возможность того, что итераторы могут быть не на 100% безопасными для копирования - например, если были тонкие проблемы с алиасами, если вы скопировали итератор, увеличили оригинал, а затем попытались использовать копию - вот почему этот код чувствует себя всегда - немного отрывочно для меня.

Ответы [ 2 ]

0 голосов
/ 03 мая 2018

Хотя это не совсем понятно, но я бы вообще не рекомендовал хранить итераторы.

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

И это вторая проблема: если вы обращаетесь к контейнеру и только читаете из него, ваш код в порядке. Если ваш код изменяет контейнер между вызовами, есть вероятность, что ваш итератор будет признан недействительным. Различные контейнеры имеют разную семантику, когда итератор признан недействительным, но мое практическое правило заключается в том, что после изменения размера контейнера все итераторы становятся недействительными.

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

0 голосов
/ 03 мая 2018

Зависит от типа итератора, но вполне подходит для std::map::iterator.

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

std::map::iterator удовлетворяет требованиям концепции BidirectionalIterator. Это означает, что, среди прочего, увеличение копии итератора не сделает недействительным оригинал. Это означает, что выполнение prev = it; ++it не сделает недействительным prev, поэтому ваш алгоритм четко определен.

Однако это относится не ко всем итераторам. Концепция InputIterator не дает этой гарантии. Обратите внимание на постусловие для ++i:

Постусловие: любые копии предыдущего значения i больше не должны быть разыменованными или находиться в домене ==.

Мне неизвестны какие-либо итераторы в стандартной библиотеке, которые потеряют свое значение, если вы увеличите их копию, но я лично создал именно такой тип итератора (он перебирает записи в архивном файле, и предыдущая запись больше не читается при переходе к следующей).

См. Также понятия Iterator, ForwardIterator, RandomAccessIterator и OutputIterator. Все они строятся друг на друге.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...