Как *it++ действителен для итераторов вывода?

В примере кода я часто вижу такой код, как *it++ для итераторов вывода. Выражение *it++ создает копию it, увеличивает it, а затем возвращает копию, которая окончательно разыменовывается. Насколько я понимаю, создание копии выходного итератора делает недействительным источник. Но тогда приращение it, которое выполняется после создания копии, будет незаконным, верно? Является ли мое понимание итераторов вывода ошибочным?


person fredoverflow    schedule 22.10.2010    source источник
comment
Почему копирование итератора (выходного или иного) делает его недействительным? Разве мы не делаем это обычно, когда говорим что-то вроде: vector<int>::iterator it(container.begin());   -  person Adrian McCarthy    schedule 23.10.2010
comment
@Adrian: Например, документация SGI говорит: Должен быть только один активная копия одного выходного итератора в любой момент времени. То есть: после создания и использования копии x выходного итератора y исходный выходной итератор y больше не должен использоваться. Однако я не совсем уверен, что означает использование итератора вывода в этом контексте. Разыменование? Увеличение?   -  person fredoverflow    schedule 23.10.2010
comment
Я вижу, что после out_iterator a(b); b может быть недопустимым в документации SGI, как вы сказали, но в C++03 на это не намекает. Но поскольку и на странице SGI, и в C++03 явно указано, что выражение *it++ = value должно быть допустимым, это требование перевешивает любые другие проблемы, связанные с таким использованием.   -  person aschepler    schedule 23.10.2010


Ответы (5)


Выражение *it++ не (должно) делать его копию, не увеличивать его и т. д. Это выражение допустимо только для удобства, так как следует обычной семантике . Только operator= выполняет фактическую работу. Например, в g++ реализация ostream_iterator, operator*, operator++ и operator++(int) делает только одну вещь: return *this (другими словами, ничего!). Мы могли бы написать, например:

it = 1;
it = 2;
*it = 3;
++it = 4;

Вместо: *it++ = 1; *it++ = 2; *it++ = 3; *it++ = 4;

person rafak    schedule 23.10.2010
comment
Это ответ, который вызвал у меня ага-момент, поэтому я выбираю его как правильный. - person fredoverflow; 23.10.2010
comment
Вероятно, вам следует пояснить, что первый пример работает только потому, что вы знаете реализацию, а второй пример — это правильный способ использования выходного итератора. - person Dingo; 23.10.2010
comment
@Dingo: если я правильно понимаю стандарт, он не зависит от реализации, но хорошо определен для ostream_iterator. Так что в общем случае не может требоваться, чтобы *it++ делал копию и т.д. - person rafak; 28.10.2010
comment
Стандарт говорит в разделе об osteram_iterator: Его можно использовать только в качестве выходного итератора в таких ситуациях, как while (first != last) *result++ = *first++; Тот факт, что ваши примеры, it = 2;, *it = 3; и ++it = 4;, работают из-за реализации, а не потому, что это указано в стандарте. - person Dingo; 29.10.2010
comment
@Dingo: Хорошо, я не знал, что фраза, которую вы цитируете, означает, что это единственно возможное использование, вы не можете использовать *result или result++ ... Когда я прочитал часть операции ostream_iterator (24.5.2.2), я интерпретировал отсутствие Спецификация Эффектов как: ничего не делает. - person rafak; 29.10.2010

Стандарт требует, чтобы *r++ = t работало для итераторов вывода (24.1.2). Если это не работает, это не итератор вывода по стандартному определению.

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

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

Вот почему *r++ = t работает. Создается копия исходного итератора, исходный итератор разыменовывается, а копия увеличивается. Исходный итератор больше никогда не будет использоваться, а копия больше не будет ссылаться на то же значение.

person Dingo    schedule 22.10.2010
comment
@Seth Нет. В исходном вопросе указывались итераторы вывода. - person Dingo; 23.10.2010
comment
@Seth: Нет, *r++ = t - это то, как вы используете итератор вывода. Наоборот даже не требуется компилировать. - person aschepler; 23.10.2010
comment
+1 Определенно более правильный, чем мой ответ, и хорошее объяснение того, что происходит. - person Omnifarious; 23.10.2010

Выходные итераторы просто не работают как обычные итераторы, и их интерфейс указан так, что их можно использовать в выражениях, подобных указателям (*it++ = x), с полезными результатами.

Как правило, operator*(), operator++() и operator++(int) все возвращают *this в качестве ссылки, а итераторы вывода имеют магический operator=, который выполняет ожидаемую операцию вывода. Поскольку вы не можете читать из выходного итератора, тот факт, что operator*() и т. д. не работают, как для других итераторов, не имеет значения.

person CB Bailey    schedule 22.10.2010

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

Копирование выходного итератора не делает скопированный итератор недействительным. Реальное ограничение довольно простое: вы должны разыменовывать заданное значение выходного итератора только один раз. Однако наличие двух копий одновременно — это нормально, если вы разыменовываете только одну из них, пока они имеют одинаковое значение. В случае, подобном тому, когда вы разыменовываете один, затем отбрасываете его значение и увеличиваете другой, но разыменовываете его только после того, как произошло приращение, все прекрасно.

person Jerry Coffin    schedule 22.10.2010

Разве итератор не просто указатель? Увеличение, а затем разыменование просто переходит к следующему элементу.

person Alexander Rafferty    schedule 22.10.2010
comment
Их можно разыменовывать, как указатель, это может сбить вас с толку, но нет, они не только указатели, они содержат указатель на элемент, среди прочего. Вот пример реализации итератора: accu-usa.org/Listings /2000-04-Listing01.html - person Daniel Rodriguez; 23.10.2010
comment
Ах я вижу. Я никогда не видел смысла в итераторах, и они мне никогда не были нужны. - person Alexander Rafferty; 23.10.2010