Iterator questions...

B

barcaroller

I've noticed that neither container.begin() nor container.end() return
an error when the container is empty, so I get a nasty segfault when I
dereference the iterator. Do I have to check if the container is
empty every time I use an iterator, or am I missing something?

Speaking of iterators: if I initialize 'iter = container.end()', can I
count on iter to stay equal to container.end() even if elements are
deleted and added to the container?
 
K

Kai-Uwe Bux

barcaroller said:
I've noticed that neither container.begin() nor container.end() return
an error when the container is empty,

Why should they? Empty ranges are perfectly valid and std::sort() will
happily sort an empty range.
so I get a nasty segfault when I dereference the iterator.

Dereferencing an invalid iterator is undefined behavior. You got lucky that
your platform did the right thing and alerted you of the bug.
Do I have to check if the container is
empty every time I use an iterator,

You have to ensure that the iterator is valid before you dereference it. If
you don't know whether the container is empty, you will have to check.
or am I missing something?

No, you seem to be spot on.

Speaking of iterators: if I initialize 'iter = container.end()', can I
count on iter to stay equal to container.end() even if elements are
deleted and added to the container?

That depends on the container: if I recall correctly, only std::list and the
associative containers make that kind of guarantee.


Best

Kai-Uwe Bux
 
J

Jim Langston

barcaroller said:
I've noticed that neither container.begin() nor container.end() return
an error when the container is empty, so I get a nasty segfault when I
dereference the iterator. Do I have to check if the container is
empty every time I use an iterator, or am I missing something?

You are missing something. For an empty containter .begin() == .end()

Aren't you checking to make sure your iteratores aren't equal to .end()?

One of the most common uses of .begin() and .end() is in a for loop, such
as:

std::list<int> Data;
for ( std::list<int>::iterator it = Data.begin(); it != Data.end(); ++it )
{
// it is guaranteed to be pointing to an item in the list
}

Other uses include passing them to algorithms, also as result of .find()
etc..

But in all cases you must ensure that your iterator != .end()
Speaking of iterators: if I initialize 'iter = container.end()', can I
count on iter to stay equal to container.end() even if elements are
deleted and added to the container?

Hmm.. I'm not sure. I do know that some operations on containers can
invalidate iterators. I'm fairly sure that this includes .end() which
points one past the last item in the container.
 
P

Pascal J. Bourguignon

Jim Langston said:
You are missing something. For an empty containter .begin() == .end()

Even more, .end() is always an invalid iterator that cannot be
derefenced!
[...]
But in all cases you must ensure that your iterator != .end()
Speaking of iterators: if I initialize 'iter = container.end()', can I
count on iter to stay equal to container.end() even if elements are
deleted and added to the container?

Hmm.. I'm not sure. I do know that some operations on containers can
invalidate iterators. I'm fairly sure that this includes .end() which
points one past the last item in the container.

Yes, the old value of .end() may become valid, which would be invalid,
for an iterator that must always be invalid :)
 
J

Juha Nieminen

barcaroller said:
I've noticed that neither container.begin() nor container.end() return
an error when the container is empty, so I get a nasty segfault when I
dereference the iterator. Do I have to check if the container is
empty every time I use an iterator, or am I missing something?

What do you suggest would happen if you dereference the begin()
iterator of an empty container?

You have to check if the iterator equals the end() iterator before
dereferencing it. Dereferencing the end() iterator is invalid.
 
B

barcaroller

Jim Langston said:
You are missing something. For an empty containter .begin() == .end()

Aren't you checking to make sure your iteratores aren't equal to .end()?

One of the most common uses of .begin() and .end() is in a for loop, such
as:

std::list<int> Data;
for ( std::list<int>::iterator it = Data.begin(); it != Data.end(); ++it )
{
// it is guaranteed to be pointing to an item in the list
}

Actually, I'm not using the iterator in a loop. Briefly, I save the
iterator of the last successful find(). Before I call find() again, I first
check if the last iterator points to the info that I need. If not, then I
call find(). In essence, I'm caching the iterator to improve performance,
by avoiding a call to find().

Note: where find() is either a member function or an algorithm.

However, caching iterators has its own problems. Initialization,
invalidation, etc.
 
B

barcaroller

One of the most common uses of .begin() and .end() is in a for loop, such
as:

std::list<int> Data;
for ( std::list<int>::iterator it = Data.begin(); it != Data.end(); ++it )
{
// it is guaranteed to be pointing to an item in the list

}


Actually, I'm not using the iterator in a loop. Briefly, I save the
iterator of the last successful find(). Before I call find() again, I
first check if the last iterator points to the info that I need. If
not, then I
call find(). In essence, I'm caching the iterator to improve
performance, by avoiding a call to find().

Note: where find() is either a member function or an algorithm.

However, caching iterators has its own problems. Initialization,
invalidation, etc.
 
B

barcaroller

What do you suggest would happen if you dereference the begin()
iterator of an empty container?

As I said earlier, I get a segfault. I would expect begin() to return
the iterator equivalent of NULL or throw an exception or something.
If the container is empty, begin() should not return a valid value.
But that's just my opinion.

You have to check if the iterator equals the end() iterator before
dereferencing it. Dereferencing the end() iterator is invalid.

Yes, that's right. Please see my response to Jim.
 
J

Jim Langston

barcaroller said:
Actually, I'm not using the iterator in a loop. Briefly, I save the
iterator of the last successful find(). Before I call find() again, I
first check if the last iterator points to the info that I need. If
not, then I
call find(). In essence, I'm caching the iterator to improve
performance, by avoiding a call to find().

Note: where find() is either a member function or an algorithm.

However, caching iterators has its own problems. Initialization,
invalidation, etc.

..find() can and will return .end() if the item was not found. You need to
test for this and not attempt to dereference the iterator if it is .end().
Also be aware that you are "saving the iterator". Be sure there is no
interventing changes to the vector that can invalidate your reference.
Without seeing your actual code I can not say for certain why the
dereference is not working, only that there is something you are not taking
into account, most likely the iterator pointing to .end() or being
invalidated because of operations on the container.
 
J

Jim Langston

barcaroller said:
As I said earlier, I get a segfault. I would expect begin() to return
the iterator equivalent of NULL or throw an exception or something.
If the container is empty, begin() should not return a valid value.
But that's just my opinion.

If the container is empty .begin() will be equal to .end() which is invalid
to be referenced but can be tested against.

In a way, .end() is the iterator equivalent of NULL, as both can not be
dereference and both can be tested against.
 
B

barcaroller

barcaroller wrote:

In a way, .end() is the iterator equivalent of NULL, as both can not be
dereference and both can be tested against.

Indeed, with one exception. Unlike NULL, the value of end() can
change for some containers, making the following code invalid:

// Initialize savedlastIter
savedlastIter = container.end();

// Lots of other code

if (savedLastIter == container.end())
{
// savedLastIter has not been set yet
savedlastIter = container.find(whatIWant);

// could still be end()
}
else
{
// savedlastIter seems to have been set
if (savedlastIter->whatIwant())
// we're done
else
{
savedLastIter= find(whatIWant);

// could still be end()
}
}


This is just pseudo-code; there's lost of safety checks missing but
you get the idea.
 
B

barcaroller

I've noticed that neither container.begin() nor container.end() return
an error when the container is empty, so I get a nasty segfault when I
dereference the iterator. Do I have to check if the container is
empty every time I use an iterator, or am I missing something?

Speaking of iterators: if I initialize 'iter = container.end()', can I
count on iter to stay equal to container.end() even if elements are
deleted and added to the container?

I apologize for the duplicate messages in this thread. I don't know
why the server is doing this.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,149
Latest member
Vinay Kumar Nevatia0
Top