6

Per the (excellent) question C++ OutputIterator post-increment requirements, we observe that for a dereferenceable and incrementable value r of OutputIterator type X, and value o of appropriate type, the expression

*r++ = o;

is valid and has equivalent semantics to

X a(r);
++r;
*a = o;

However, is it still the case the a is dereference-assignable if r has been incremented more than once in the intervening period; that is, is this code valid?

X a(r);
++r;
++r;
*a = o;

It's difficult to see how operations on a value can have an effect on the validity of operations on another value, but e.g. InputIterator (24.2.3) has, under the postconditions of ++r:

Any copies of the previous value of r are no longer required either to be dereferenceable or to be in the domain of ==.

Relevant sections: 24.2.2 Iterator, 24.2.4 Output iterators, 17.6.3.1 Template argument requirements.

Also, if this is not required to be valid, are there any situations where exploiting its non-validity would aid in the implementation (w.r.t. efficiency, simplicity) of an OutputIterator type while still observing the existing requirements?

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • 1
    SGI STL (http://www.sgi.com/tech/stl/OutputIterator.html footnote 3) explicitly forbids this behavior, forcing a sequence of alternating increments and dereference-assignments; however, I can't derive that requirement from the C++ standard. Does '*only once*' in [24.2.4:2] mean exactly once or at most once? – nknight Aug 09 '12 at 17:04
  • This answer: (http://stackoverflow.com/a/4004035/985943) makes me think that this behavior is valid for an `ostream_iterator`, which only actually increments on assignment, ignoring other increment operations. However, it doesn't seem all OutputIterators must obey those semantics. – nknight Aug 09 '12 at 17:18
  • @nknight very interesting. *only once* appears 5 times in the standard, at 3.4:1 with a different meaning, and at 5.17:7, 12.8:15, 29.6.5:8 in the sense *at most once (but usually once)*. *exactly once* and *at most once* each appear 5 times. Surely if the standard intended the SGI restrictions, they'd have incorporated that language? – ecatmur Aug 09 '12 at 17:18
  • Actually it looks like someone found this in 2004: (http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#485), and the wording in n3066 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3066.html) seems to confirm the "alternating increment/dereference" requirement, don't you agree? Strange that this didn't make it into my copy of 14882:2011, when the rest of n3066 did (as far as I can tell). – nknight Aug 13 '12 at 04:57
  • 1
    @nknight and a little more digging reveals [defect 2035](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2035), which was raised after n3225 appeared without the fixes from n3066. So the *intent* of the standard is clear, it just didn't get in to the published version in time. Post as an answer and I'll accept. – ecatmur Aug 13 '12 at 09:49

1 Answers1

3

This issue was raised in 2004 as defect 485, and the wording in n3066 clarifies the issue, requiring that an output iterator need only support a sequence of alternating increments and dereference/assignments. So in your example, r need not be incrementable after the first ++r, unless there is an intervening dereference/assignment. This behavior is also required by SGI's STL (see footnote 3). As you mentioned above, n3225 appeared without the fixes from n3066, so defect 2035 was raised; but alas the fix did not make it into the published version of C++11 (ISO/IEC 14882:2011).

Furthermore, defect 2035 says that a (from X a(r++);) cannot be used like *a = 0:

"After this operation [i.e., ++r] r is not required to be incrementable and any copies of the previous value of r are no longer required to be dereferenceable or incrementable."

There are situations where this may aid the implementation (in terms of simplicity): see e.g. this question on ostream_iterator, where such (invalid) double increments are ignored simply returning *this; only a dereference/assignment causes the ostream_iterator to actually increment.

Community
  • 1
  • 1
nknight
  • 1,034
  • 8
  • 17