408. Is vector<reverse_iterator<char*> > forbidden?

Section: 25.3 [iterator.requirements] Status: NAD Editorial Submitter: Nathan Myers Opened: 2003-06-03 Last modified: 2016-01-28 10:19:27 UTC

Priority: Not Prioritized

View all other issues in [iterator.requirements].

View all issues with NAD Editorial status.


I've been discussing iterator semantics with Dave Abrahams, and a surprise has popped up. I don't think this has been discussed before.

25.3.4 [iterator.concepts] says that the only operation that can be performed on "singular" iterator values is to assign a non-singular value to them. (It doesn't say they can be destroyed, and that's probably a defect.) Some implementations have taken this to imply that there is no need to initialize the data member of a reverse_iterator<> in the default constructor. As a result, code like

  std::vector<std::reverse_iterator<char*> > v(7);

invokes undefined behavior, because it must default-initialize the vector elements, and then copy them to other storage. Of course many other vector operations on these adapters are also left undefined, and which those are is not reliably deducible from the standard.

I don't think that 24.1 was meant to make standard-library iterator types unsafe. Rather, it was meant to restrict what operations may be performed by functions which take general user- and standard iterators as arguments, so that raw pointers would qualify as iterators. However, this is not clear in the text, others have come to the opposite conclusion.

One question is whether the standard iterator adaptors have defined copy semantics. Another is whether they have defined destructor semantics: is

  { std::vector<std::reverse_iterator<char*> >  v(7); }

undefined too?

Note this is not a question of whether algorithms are allowed to rely on copy semantics for arbitrary iterators, just whether the types we actually supply support those operations. I believe the resolution must be expressed in terms of the semantics of the adapter's argument type. It should make clear that, e.g., the reverse_iterator<T> constructor is actually required to execute T(), and so copying is defined if the result of T() is copyable.

Issue 235, which defines reverse_iterator's default constructor more precisely, has some relevance to this issue. However, it is not the whole story.

The issue was whether

  reverse_iterator() { }

is allowed, vs.

  reverse_iterator() : current() { }

The difference is when T is char*, where the first leaves the member uninitialized, and possibly equal to an existing pointer value, or (on some targets) may result in a hardware trap when copied.

8.5 paragraph 5 seems to make clear that the second is required to satisfy DR 235, at least for non-class Iterator argument types.

But that only takes care of reverse_iterator, and doesn't establish a policy for all iterators. (The reverse iterator adapter was just an example.) In particular, does my function

  template <typename Iterator>
    void f() { std::vector<Iterator>  v(7); } 

evoke undefined behavior for some conforming iterator definitions? I think it does, now, because vector<> will destroy those singular iterator values, and that's explicitly disallowed.

24.1 shouldn't give blanket permission to copy all singular iterators, because then pointers wouldn't qualify as iterators. However, it should allow copying of that subset of singular iterator values that are default-initialized, and it should explicitly allow destroying any iterator value, singular or not, default-initialized or not.

Related issues: 407, 1012

[ We don't want to require all singular iterators to be copyable, because that is not the case for pointers. However, default construction may be a special case. Issue: is it really default construction we want to talk about, or is it something like value initialization? We need to check with core to see whether default constructed pointers are required to be copyable; if not, it would be wrong to impose so strict a requirement for iterators. ]

[ 2009-05-10 Alisdair provided wording. ]

The comments regarding destroying singular iterators have already been resolved. That just leaves copying (with moving implied).

[ 2009-07 Frankfurt ]

This is related to LWG 1012.

Note that there is a bug in the proposed resolution to LWG 1012. The change to [reverse.iter.con] should be modified so that the word "default" in the second sentence of the Effects clause is replaced by "value."

We believe that the proposed fix to LWG 1012 (now corrected) is sufficient to solve the problem for reverse_iterator. However, Alisdair pointed out that LWG 1012 does not solve the general problem for authors of iterator adaptors.

There are some problems with the proposed resolution. The phrase "safely copyable" is not a term of art. Also, it mentions a DefaultConstructible? concept.

Move to Review after Alisdair updates the wording.

[ 2009-07-31 Alisdair revised wording: ]

[ 2009-08-17 Alisdair and Daniel collaborate on slightly revised wording. This issue depends upon 724 ]

[ 2009-10-14 Daniel adds: ]

There is a clear dependency on 1213, because the term "singular", which is used as part of the resolution, is not properly defined yet.

[ 2009-10 Santa Cruz: ]

Moved to Open. Alisdair will provide improved wording to make this have "value semantics" and otherwise behave like a valid iterator.

[ 2010 Pittsburgh: Moved to NAD Editorial. Rationale added below. ]


Solved by N3066.

Proposed resolution:

Add a new paragrpah to Iterator concepts 25.3 [iterator.requirements] after para 5 (the one describing singular iterators)

Just as a regular pointer to an array guarantees that there is a pointer value pointing past the last element of the array, so for any iterator type there is an iterator value that points past the last element of a corresponding container. These values are called past-the-end values. Values of an iterator i for which the expression *i is defined are called dereferenceable. The library never assumes that past-the-end values are dereferenceable. Iterators can also have singular values that are not associated with any container. [Example: After the declaration of an uninitialized pointer x (as with int* x;), x must always be assumed to have a singular value of a pointer. — end example] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value and the assignment of a non-singular value to an iterator that holds a singular value. In this case the singular value is overwritten the same way as any other value. Dereferenceable values are always non-singular.

After value-initialization, any iterator that satisfies the DefaultConstructible requirements ([defaultconstructible]) shall not introduce undefined behaviour when used as the source of a copy or move operation, even if it would otherwise be singular. [Note: This guarantee is not offered for default-initialization (9.4 [dcl.init]), although the distinction only matters for types with trivial default constructors such as pointers. — end note]