views:

204

answers:

5

In example code, I often see code such as *it++ for output iterators. The expression *it++ makes a copy of it, increments it, and then returns the copy which is finally dereferenced. As I understand it, making a copy of an output iterator invalidates the source. But then the increment of it that is performed after creating the copy would be illegal, right? Is my understanding of output iterators flawed?

A: 

Isn't an iterator just a pointer? Incrementing, then dereferencing it just moves on to the next element.

Alexander Rafferty
No, iterators are not just pointers.
James McNellis
They can be dereferenced, just like a pointer, this may be casing you confusion, but no, they are not only pointers, they hold a pointer to an element, among other things. Here is a sample implementation of an iterator: http://www.accu-usa.org/Listings/2000-04-Listing01.html
Seth Illgard
Ah, I see. I've never seen the point in iterators, and I've never needed them.
Alexander Rafferty
+1  A: 

Output iterators just don't work like normal iterators and their interface is specified so that they can be used in pointer-like expressions (*it++ = x) with useful results.

Typically, operator*(), operator++() and operator++(int) all return *this as a reference and output iterators have a magic operator= which performs the expected output operation. Because you can't read from an output iterator, the fact that operator*() etc., don't work as for other iterators doesn't matter.

Charles Bailey
+8  A: 

The standard requires that *r++ = t work for output iterators (24.1.2). If it doesn't work, it's not an output iterator by the standard's definition.

It is up to the iterator implementation to make sure such statements work correctly under the hood.

The reason that you shouldn't keep multiple copies of an output iterator is that it has single pass semantics. The iterator can only be dereferenced once at each value (i.e. it has to be incremented between each dereference operation). Once an iterator is dereferenced, a copy of it cannot be.

This is why *r++ = t works. A copy is made of the orignal iterator, the original iterator is dereferenced and the copy is incremented. The original iterator will never be used again, and the copy no longer references the same value.

Dingo
Don't you mean t = *r++ ?
Seth Illgard
@Seth No. The original question specified output iterators.
Dingo
@Seth: No, `*r++ = t` is how you use an output iterator. The other way around is not even required to compile.
aschepler
+1 Definitely more correct than my answer, and a good explanation of what's going on.
Omnifarious
@Dingo, @aschepler: thanks for the information.
Seth Illgard
A: 

Looking at your comment, it seems that most of the confusion arises from the SGI documentation, which I'd say is a bit misleading on this point.

Copying an output iterator does not invalidate the copied iterator. The real limitation is pretty simple: you should only dereference a given value of output iterator once. Having two copies at a time, however, is fine as long as you only dereference once of them while they have the same value. In a case like there where you're dereferencing one, then throwing away its value, and incrementing the other but only dereferencing it after the increment has happened, it's all perfectly fine.

Jerry Coffin
+1  A: 

The expression *it++ does not (have to) make a copy of it, does not increment it, etc. This expression is valid only for convenience, as it follows the usual semantics. Only operator= does the actual job. For example, in g++ implementation of ostream_iterator, operator*, operator++ and operator++(int) do only one thing: return *this (in other words, nothing!). We could write for example:

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

Instead of: *it++ = 1; *it++ = 2; *it++ = 3; *it++ = 4;

rafak
This is the answer that gave me an aha-moment, hence I'm selecting it as the correct one.
FredOverflow
You should probably make it clear that the first example only works because you know the implementation and the second example is the proper way to use an output iterator.
Dingo
@Dingo: if I understand correctly the standard, it is not implementation dependant but well defined for `ostream_iterator`. So in the general case it can't be required that `*it++` does a copy, etc.
rafak
@rafak The standard says in the section about osteram_iterator: "Its only use is as an output iterator in situations like `while (first != last) *result++ = *first++;`" The fact that your examples, `it = 2;`, `*it = 3;`, and `++it = 4;`, work is because of the implementation, not because the standard specifies it.
Dingo
@Dingo: OK, I didn't know the phrase you cite meant "it is the only possible use, you can't use `*result` or `result++`... When I read the "ostream_iterator operation" part (24.5.2.2), I interpreted the absence of "Effects" specification as: "does nothing".
rafak