Topic: A question about empty ranges
Author: postmast.root.admi.gov@iname.com (blargg) (postmaster@nospam.gov)
Date: 1999/09/27 Raw View
In article <37ECCB17.BCD8782A@ihug.co.nz>, Ross Smith <ross.s@ihug.co.nz> wrote:
> "James Kuyper Jr." wrote:
> >
> > The standard does say that if a container is empty, then begin() and
> > end() compare equal. However, something I haven't found is the reverse
> > statement: that if begin()==end(), the container must be empty. I was
> > wondering about this, because I was thinking about defining a
> > CircularList<> container, similar to std::list<>, but with the property
> > that begin() ie always equivalent to end(). An empty CircularList can be
> > recognized either by checking size(), or testing whether begin()==
> > CircularList::iterator(). All algorithms working on such a container
> > would have to perform that check.
> >
> > Such a container could meet all of the sequence requirements given in
> > section 23.1.1, including the same optional requirements that
> > std::list<> is required to meet, but I wouldn't expect any standard
> > library function that takes iterator range arguments to work properly
> > when given a range of [circlist.begin(), circlist.end()). Annoying.
>
> My solution to that was to have begin() return a special "magic value"
> iterator that dereferences to the same element as end() but doesn't
> compare equal to it.
I haven't had any problem with this.
Here is a *very* simple intrusive circular doubly-linked list that
illustrates the important points (I compiled and ran it too). The property
I like about it is that there are no "if" statements in its
implementation.
struct Link {
Link* next;
Link* prev;
};
template<class T>
class List {
Link list;
public:
List() {
list.next = &list;
list.prev = &list;
}
class iterator {
Link* cur;
iterator( Link* l ) : cur( l ) { }
friend class List<T>;
public:
T* operator -> () { return static_cast<T*> (cur); }
iterator& operator ++ () {
cur = cur->next;
return *this;
}
friend bool operator == ( iterator const& x, iterator const& y ) {
return x.cur == y.cur;
}
friend bool operator != ( iterator const& x, iterator const& y ) {
return x.cur != y.cur;
}
};
iterator begin() const { return list.next; }
iterator end() const { return const_cast<Link*> (&list); }
void insert( T* t, iterator next ) {
Link* l = t;
l->prev = next.cur->prev;
next.cur->prev = l;
l->prev->next = l;
l->next = next.cur;
}
void erase( iterator pos ) {
pos.cur->prev->next = pos.cur->next;
pos.cur->next->prev = pos.cur->prev;
}
};
// demo/test
struct Foo : Link {
char val;
Foo( char c ) : val( c ) { }
};
void check_list( List<Foo> const& list, char const* expected ) {
for ( List<Foo>::iterator cur( list.begin() ); cur != list.end(); ++cur )
assert( cur->val == *expected++ );
assert( *expected == '\0' );
}
int main() {
List<Foo> list;
check_list( list, "" );
assert( list.begin() == list.end() );
list.insert( new Foo( '1' ), list.begin() );
check_list( list, "1" );
assert( list.begin() != list.end() );
list.insert( new Foo( '2' ), list.begin() );
check_list( list, "21" );
list.insert( new Foo( '3' ), list.end() );
check_list( list, "213" );
list.erase( list.begin() );
check_list( list, "13" );
List<Foo>::iterator i( list.begin() );
++i;
list.erase( i );
check_list( list, "1" );
list.erase( list.begin() );
check_list( list, "" );
}
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1999/09/27 Raw View
Ross Smith wrote:
>
> "James Kuyper Jr." wrote:
> >
> > I was wondering about this, because I was thinking about defining a
> > CircularList<> container, similar to std::list<>, but with the property
> > that begin() ie always equivalent to end().
> >
> > Such a container could meet all of the sequence requirements given in
> > section 23.1.1, including the same optional requirements that
> > std::list<> is required to meet, but I wouldn't expect any standard
> > library function that takes iterator range arguments to work properly
> > when given a range of [circlist.begin(), circlist.end()). Annoying.
>
> My solution to that was to have begin() return a special "magic value"
> iterator that dereferences to the same element as end() but doesn't
> compare equal to it.
If anything, the end() iterator should be "magic", because one normally
doesn't expect to be able to dereference end().
--
Ciao, Paul D. DeRocco
Paul mailto:pderocco@ix.netcom.com
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1999/09/27 Raw View
Aldo Aleardi wrote:
>
> If an empty range [i,i), where i is a valid dereferenceable iterator,
> is a valid range, can I assume that passing it to functions in the
> standard library is always a no-op or in some cases this will result
> in undefined behavior?
An empty range is valid. Notice that min_element and max_element, which are
logically meaningless if given two identical iterators representing an
empty range, return that iterator in this case, which can't necessarily be
dereferenced.
--
Ciao, Paul D. DeRocco
Paul mailto:pderocco@ix.netcom.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "James Kuyper Jr." <kuyper@wizard.net>
Date: 1999/09/25 Raw View
Colin Rafferty wrote:
>
> Aldo Aleardi writes:
>
> > 6
> > An iterator j is called reachable from an iterator i if and only if
> > there is a finite sequence of applications of the expression
> > ++i that makes i == j.
>
> > 7
> > ....A range [i, i) is an empty range; ....
>
> > ....Range [i, j) is valid if and only if j is reachable from i.
>
> > I cannot understand from the above statements if an empty range
> > is a valid range.
>
> It is a valid range. As described in (6), there is a finite sequence
> of applications of the expressoin ++i that makes i == i.
>
> Specifically, it takes zero applications of ++i.
>
> Another way to convince yourself that [i, i) is valid is because an
> empty vector's begin() and end() are the same iterator, and you should
> certainly be able to assume that it is legal to use them.
The standard does say that if a container is empty, then begin() and
end() compare equal. However, something I haven't found is the reverse
statement: that if begin()==end(), the container must be empty. I was
wondering about this, because I was thinking about defining a
CircularList<> container, similar to std::list<>, but with the property
that begin() ie always equivalent to end(). An empty CircularList can be
recognized either by checking size(), or testing whether begin()==
CircularList::iterator(). All algorithms working on such a container
would have to perform that check.
Such a container could meet all of the sequence requirements given in
section 23.1.1, including the same optional requirements that
std::list<> is required to meet, but I wouldn't expect any standard
library function that takes iterator range arguments to work properly
when given a range of [circlist.begin(), circlist.end()). Annoying.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Colin Rafferty <craffert@ms.com>
Date: 1999/09/25 Raw View
Aldo Aleardi writes:
> 6
> An iterator j is called reachable from an iterator i if and only if
> there is a finite sequence of applications of the expression
> ++i that makes i == j.
> 7
> ....A range [i, i) is an empty range; ....
> ....Range [i, j) is valid if and only if j is reachable from i.
> I cannot understand from the above statements if an empty range
> is a valid range.
It is a valid range. As described in (6), there is a finite sequence
of applications of the expressoin ++i that makes i == i.
Specifically, it takes zero applications of ++i.
Another way to convince yourself that [i, i) is valid is because an
empty vector's begin() and end() are the same iterator, and you should
certainly be able to assume that it is legal to use them.
> If an empty range [i,i), where i is a valid dereferenceable iterator,
> is a valid range, can I assume that passing it to functions in the
> standard library is always a no-op or in some cases this will result
> in undefined behavior?
You can assume neither.
You cannot assume that it is a no-op, becaues the standard doesn't say
that this optimization is required.
You cannot assume that it will result in undefined behavior, because
it is a valid range.
--
Colin
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Aldo Aleardi <aldo.a@interplanet.it>
Date: 1999/09/24 Raw View
from ISO/IEC 14882:1998(E)
24.1 Iterator requirements 24 Iterators library
....
6
An iterator j is called reachable from an iterator i if and only if
there is a finite sequence of applications of the expression
++i that makes i == j. If j is reachable from i, they refer to the
same container.
7
Most of the library's algorithmic templates that operate on data
structures have interfaces that use ranges.
A range is a pair of iterators that designate the beginning and end of
the computation. A range [i, i) is an empty range; in general, a range
[i, j) refers to the elements in the data structure starting with the
one pointed to by i and up to but not including the one pointed to by
j. Range [i, j) is valid if and only if j is reachable from i. The
result of the application of functions in the library to invalid
ranges is undefined.
....
I cannot understand from the above statements if an empty range
is a valid range.
If an empty range [i,i), where i is a valid dereferenceable iterator,
is a valid range, can I assume that passing it to functions in the
standard library is always a no-op or in some cases this will result
in undefined behavior?
TIA, Aldo.
--
Is everybody in? Is everybody in?
The ceremony is about to begin.
(Jim Morrison)
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]