tags:

views:

78

answers:

3

In my code, I have the following:

ostream_iterator<double> doubleWriter(cout, " ~ ");
// ...
*doubleWriter = 1.1;
doubleWriter++;
*doubleWriter = 2.2;
*doubleWriter = 3.3; // shouldn't 2.2 be overwritten?
doubleWriter++;
*doubleWriter = 44.2;

cout << endl << endl;

I expected it to output this:

1.1 ~ 3.3 ~ 44.2 ~ 

Instead, the output was this:

1.1 ~ 2.2 ~ 3.3 ~ 44.2 ~ 

Why does this happen? It would seem to me that I overwrite 2.2 and stick 3.3 in its spot, since I didn't increment. Is incrementation an optional step?

+3  A: 

The ostream_iterator is an output iterator.
http://www.sgi.com/tech/stl/OutputIterator.html

One of the requirements of the output iterator is that between each assignment to the stream that the ++ operator (pre or post) is used. Thus it is undefined behavior to assign to the stream twice in a row without incrementing the iterator.

Dereference assignment:     *x = t

Pre-Conditions:             x is dereferenceable:

                            If there has been a previous assignment through x,
                            then there has been an intervening increment.

So technically what you are doing above is undefined behavior.
Thus the implementation is allowed to provide the output you describe.

See Expression semantics for de-re assignament (pre-conditions) also See Note [3] in the linked page above.

For the offical standard quote:

http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2010/n3090.pdf (latest draft)
Section 24.2.4: Paragraph 2 (emphasis by me)

[ Note: The only valid use of an operator* is on the left side of the assignment statement. Assignment through the same value of the iterator happens only once. Algorithms on output iterators should never attempt to pass through the same iterator twice. They should be single pass algorithms. Equality and inequality might not be defined. Algorithms that take output iterators can be used with ostreams as the destination for placing data through the ostream_iterator class as well as with insert iterators and insert pointers. —end note ]

I disagree with AndreyT interpretation of defect report 485:

a) That each value of the output iterator is written to: The standard allows: ++x; ++x; ++x;
b) That assignments to the output iterator are made in order X a(x); ++a; *a=1; *x=2; is allowed
c) Chains of output iterators cannot be constructed: X a(x); ++a; X b(a); ++b; X c(b); ++c; is allowed, and under the current wording (I believe) x,a,b,c could be written to in any order.

Problem (a) and (c) are not relevant.
Problem (b) refers to a situations where there are multiple iterators involved.

Note in (b) we are using iterator a (copy of x) and iterator x. The defect is that the current standard does not requrie 'x' to be increment before use; after 'a' has been assigned through. Which is not the situation in this context.

Martin York
So incrementation is automatic? I ask because the book I'm reading makes religious use of incrementation, always making sure that after a value is written that the iterator is incremented.
adam_0
No it is a requirement to use the increment between each assignment otherwise the output is undefined.
Martin York
A: 

The ostream iterator behavior is to always move the output position. That's what you usually want.

Jay
+3  A: 

For standard ostream_iterator, operator ++ (both prefix and postfix) is a no-op. It does nothing and makes no difference.

There's no need to do the ++ manually, since the iterator always "advances" when you output something through it.

P.S. According to library Defect Report 485, the intent was to require that assignment and increments alternate. This requirement apparently haven't made it into the current version of the standard. But in the future revisions, it will be included. In other words, as others already noted, the proper way to use output iterator is to alternate assignments and increments.

AndreyT
That just happens to be an implementation detail of the ostream_iterator you are using (and probably the way that all ostream_iterators behave). BUT it is undefined behavior if you do not use the ++ opertator between assignments through the iterator
Martin York
Actually I'm pretty sure that Andrey is right - my book says that the ++ is indeed a no-op. Also, the quote in your answer says that if you just did *x = t, then "there has been an intervening increment" (meaning that it's been done for you) - which would be consistent with Andrey's answer.
adam_0
@Martin York: Could you point me to the right place in the standard? I looked through the specification and I don't see it.
AndreyT
http://www.sgi.com/tech/stl/OutputIterator.html: See Note 3 at the bottom.
Martin York
@Martin York: Great, but I don't see anything like this in the standard. Without it, it actually appears to be a requirement of that particular implementation (SGI).
AndreyT
adam_0: "There is an intervening increment" does not mean it is does for you. In it is in the pre-conditions of the expressions semantics meaning that this is a pre-condition that you must guarantee has been fulfilled before you can legally (without undefined behavior) use the expression.
Martin York
@AndreyT: Do you have a link to the standard you are reading? The SGI version is the original documentation for the STL and is what the standard is based on.
Martin York
@Martin York: I'm reading the PDF documents for C++98 and C++03 purchased from ANSI Web store.
AndreyT
@Martin York: You are right, but it is still not in the standard. See the DR#485 http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#485
AndreyT
@AudreyT: You are using the Issues list. See: http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2010/n3090.pdf. Section 24.2.4 Paragraph 2: <quote>Note: The only valid use of an operator* is on the left side of the assignment statement. Assignment through the same value of the iterator happens only once</quote>
Martin York
@audreyT: Defect report 485 definitely does not state what you claim it does above. In the defect list it is specifying that if we have two or more output iterators (constructed via copying) then we have inconsistencies; as assignment through one iterator does not require any of the other iterators to be incremented before they can be be assigned through.
Martin York
@Martin York: Please, take a look at the proposed modifications to the Output Iterator requirements table at the link. Namely, the post-conditions. It clearly states that once you dereference (i.e. assign through) an iterator, it is not guaranteed to be dereferenceable any more, but guaranteed to be incrementable. And once you increment an iterator, it is not guaranteed to be incrementable anymore, but is guaranteed to be dereferenceable. This update will essentially force you to alternate increments and dereferences.
AndreyT
AndreyT: Yes I have read it. This wording changes is designed to solve issue (a) in 485. It still does not change that the defect report is talking about situations with two different iterators. The pre-conditions still hold when one iterator is being used.
Martin York
@Martin York: I still don't understand. Firstly, issue (1) in 485 is about *one* iterator. Secondly, I don't really care what specific problem (or problems) lead to the issue 485. What I do care about is that the changes made to the table 101 immediately and directly apply to our case.
AndreyT
@AndreyT: <quote>While it requires that each iterator is progressed through only once and that each iterator is written to only once, it does not require the following things</quote> It says what I have been saying (there must be an increment between each assignment). It then goes on to say that this is not enough and the issues (a) (b) and (c) are allowed but probably not what is wanted by the standard and these should be closed. Non of (a) (b) or (c) are relevant to the discussion we are having, thus 485 is not relevant.
Martin York