Section: 24.3.4 [iterator.concepts] Status: CD1 Submitter: Beman Dawes Opened: 1999-11-03 Last modified: 2016-01-28
Priority: Not Prioritized
View all other issues in [iterator.concepts].
View all issues with CD1 status.
Discussion:
Is a pointer or reference obtained from an iterator still valid after destruction of the iterator?
Is a pointer or reference obtained from an iterator still valid after the value of the iterator changes?
#include <iostream> #include <vector> #include <iterator> int main() { typedef std::vector<int> vec_t; vec_t v; v.push_back( 1 ); // Is a pointer or reference obtained from an iterator still // valid after destruction of the iterator? int * p = &*v.begin(); std::cout << *p << '\n'; // OK? // Is a pointer or reference obtained from an iterator still // valid after the value of the iterator changes? vec_t::iterator iter( v.begin() ); p = &*iter++; std::cout << *p << '\n'; // OK? return 0; }
The standard doesn't appear to directly address these questions. The standard needs to be clarified. At least two real-world cases have been reported where library implementors wasted considerable effort because of the lack of clarity in the standard. The question is important because requiring pointers and references to remain valid has the effect for practical purposes of prohibiting iterators from pointing to cached rather than actual elements of containers.
The standard itself assumes that pointers and references obtained from an iterator are still valid after iterator destruction or change. The definition of reverse_iterator::operator*(), 24.5.1.5 [reverse.iter.conv], which returns a reference, defines effects:
Iterator tmp = current; return *--tmp;
The definition of reverse_iterator::operator->(), [reverse.iter.op.star], which returns a pointer, defines effects:
return &(operator*());
Because the standard itself assumes pointers and references remain valid after iterator destruction or change, the standard should say so explicitly. This will also reduce the chance of user code breaking unexpectedly when porting to a different standard library implementation.
Proposed resolution:
Add a new paragraph to 24.3.4 [iterator.concepts]:
Destruction of an iterator may invalidate pointers and references previously obtained from that iterator.
Replace paragraph 1 of 24.5.1.5 [reverse.iter.conv] with:
Effects:
this->tmp = current; --this->tmp; return *this->tmp;[Note: This operation must use an auxiliary member variable, rather than a temporary variable, to avoid returning a reference that persists beyond the lifetime of its associated iterator. (See 24.3.4 [iterator.concepts].) The name of this member variable is shown for exposition only. --end note]
[Post-Tokyo: The issue has been reformulated purely in terms of iterators.]
[Pre-Toronto: Steve Cleary pointed out the no-invalidation assumption by reverse_iterator. The issue and proposed resolution was reformulated yet again to reflect this reality.]
[Copenhagen: Steve Cleary pointed out that reverse_iterator
assumes its underlying iterator has persistent pointers and
references. Andy Koenig pointed out that it is possible to rewrite
reverse_iterator so that it no longer makes such an assupmption.
However, this issue is related to issue 299. If we
decide it is intentional that p[n]
may return by value
instead of reference when p
is a Random Access Iterator,
other changes in reverse_iterator will be necessary.]
Rationale:
This issue has been discussed extensively. Note that it is
not an issue about the behavior of predefined iterators. It is
asking whether or not user-defined iterators are permitted to have
transient pointers and references. Several people presented examples
of useful user-defined iterators that have such a property; examples
include a B-tree iterator, and an "iota iterator" that doesn't point
to memory. Library implementors already seem to be able to cope with
such iterators: they take pains to avoid forming references to memory
that gets iterated past. The only place where this is a problem is
reverse_iterator
, so this issue changes
reverse_iterator
to make it work.
This resolution does not weaken any guarantees provided by
predefined iterators like list<int>::iterator
.
Clause 23 should be reviewed to make sure that guarantees for
predefined iterators are as strong as users expect.