Стоит ли создавать некопируемый итератор STL?

В большинстве случаев итераторы STL являются CopyConstructable, потому что некоторые алгоритмы STL требуют этого для повышения производительности, например std::sort.

Тем не менее, я работал над домашним проектом, чтобы обернуть API FindXFile (о котором уже спрашивали), но проблема в том, что вокруг этого API невозможно реализовать копируемый итератор. Дескриптор поиска ни в коем случае нельзя дублировать — DuplicateHandle специально запрещает передавать ему такие типы дескрипторов. И если вы просто поддерживаете счетчик ссылок на дескриптор поиска, то однократное приращение любой копии приводит к приращению всех копий - ясно, что это не то, что должен делать итератор, созданный копией.

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

Что меньшее из двух зол?


person Billy ONeal    schedule 02.04.2010    source источник
comment
Педантично говоря, он не будет быть итератором STL, если его нельзя скопировать. :)   -  person jalf    schedule 03.04.2010


Ответы (1)


Входной итератор, который не является прямым итератором, можно копировать, но вы можете «использовать» только одну из копий: увеличение любой из них делает недействительными другие (разыменование одного из них не делает недействительными другие). Это позволяет передавать его алгоритмам, но алгоритм должен завершиться за один проход. Вы можете сказать, какие алгоритмы в порядке, проверив их требования - например, copy требует только InputIterator, тогда как adjacent_find требует ForwardIterator (первый, который я нашел).

Мне кажется, это описывает вашу ситуацию. Просто скопируйте дескриптор (или что-то, что ссылается на дескриптор), не дублируя его.

Пользователь должен понимать, что это всего лишь InputIterator, но на практике это не имеет большого значения. istream_iterator то же самое и по той же причине.

Оглядываясь назад в С++ 11, было бы почти разумно требовать, чтобы InputIterators были перемещаемыми, но не требовали, чтобы их можно было копировать, поскольку дубликаты в любом случае имеют ограниченное использование. Но это «ограниченное использование», а не «бесполезность», и в любом случае уже слишком поздно удалять функциональность из InputIterator, учитывая, сколько кода зависит от существующего определения.

person Steve Jessop    schedule 02.04.2010
comment
Я не думаю, что копирование входного итератора аннулирует (не в смысле этого слова в стандарте) исходный итератор. У него есть побочные эффекты (то есть результат *it1 может быть другим, если он вызывается до или после ++it2), но ни одна из копий AFAIK не считается недействительной. Таблица 72 в стандарте в §24.1.1/2 определяет a = u как допустимую операцию, а постусловия не упоминают о какой-либо недействительности. В §24.1.1/3 рассматривается конкретное поведение, присутствующее только во входных итераторах: a == b делает не подразумевает ++a == ++b (т.е. допустимо только в однопроходных алгоритмах) - person David Rodríguez - dribeas; 02.04.2010
comment
Чтобы сохранить семантику входного итератора, дескриптор должен совместно использоваться различными экземплярами скопированных итераторов, и вы должны гарантировать, что дескриптор освобождается в соответствующее время, а не раньше или позже. - person David Rodríguez - dribeas; 02.04.2010
comment
@ Дэвид: Да. Обычно я делаю это, используя идиому pimpl с некопируемым внутренним классом и boost::shared_ptr. - person Billy ONeal; 03.04.2010
comment
@dribeas: правильно. Как я уже говорил, копирование входного итератора разрешено. В 24.1.1 в таблице 24 указано пост-условие для operator++: Любые копии предыдущего значения r больше не должны быть разыменуемыми или находиться в домене ==. Я думаю, что аннулирование является разумным обобщением этого, хотя на самом деле это не говорит о том, что вы не можете увеличить копию предыдущего значения, а затем разыменовать это. Он говорит (1), что он не обязательно дает тот же результат, что и тот, который вы увеличили первым, и (2) что вы никогда не должны этого делать. - person Steve Jessop; 03.04.2010