BSchlinker
BSchlinker

Reputation: 3481

Iterator for second to last element in a list

I currently have the following for loop:

for(list<string>::iterator jt=it->begin(); jt!=it->end()-1; jt++)

I have a list of strings which is in a larger list (list<list<string> >). I want to loop through the contents of the innerlist until I get to the 2nd to last element. This is because I have already processed the contents of the final element, and have no reason to process them again.

However, using it->end()-1 is invalid -- I cannot use the - operator here. While I could use the -- operator, this would decrement this final iterator on each cycle.

I believe a STL list is a doubly linked list, so from my perspective, it should be possible to do this.

Advice? Thanks in advance

Upvotes: 18

Views: 24038

Answers (5)

Jay Gowdy
Jay Gowdy

Reputation: 83

In c++11 and later, the best answer appears to be to use std::prev

for(iterator i = lst.begin(); i != std::prev(lst.end()); ++i) {
    // do
    // stuff
}

The documentation for std::prev on http://en.cppreference.com/w/cpp/iterator/prev says,

Although the expression --c.end() often compiles, it is not guaranteed to do so: c.end() is an rvalue expression, and there is no iterator requirement that specifies that decrement of an rvalue is guaranteed to work. In particular, when iterators are implemented as pointers, --c.end() does not compile, while std::prev(c.end()) does.

I believe std::prev() on an empty list is undefined, so you may need to wrap this in a !i.empty() condition

Upvotes: 7

Dennis Zickefoose
Dennis Zickefoose

Reputation: 10969

Requisite recommendation to use the standard library:

std::for_each(lst.begin(), --lst.end(), process);

If you don't want to hassle with creating a functor [I almost never do], and you can't use reverse iterators, hoist the end check out of the loop:

for(iterator i = lst.begin(), j = --lst.end(); i != j; ++i) {
    // do
    // stuff
}

Or, you can just trust the optimizer to recognize that it doesn't have to keep recreating the end condition, and do the hoisting itself. How reliable this is depends on the list implementation, and how complex your loop code is, and how good your optimizer is.

At any rate, just do what is easiest for you to understand, and worry about performance after you're done.

Upvotes: 13

lord.didger
lord.didger

Reputation: 1407

what about a reverse iterator?

for(list<string>::reverse_iterator jt=++(it->rbegin()); jt!=it->rend(); jt++)

Upvotes: 4

Naszta
Naszta

Reputation: 7744

List iterator is not random iterator. You should do the following:

if ( ! it->empty() )
{
  list<string>::iterator test = it->end();
  --test;
  for( list<string>::iterator jt = it->begin(); jt != test; ++jt )
  {
  ...
  }
}

One more thing: use ++jt against jt++. jt++ source code usually looks something like this:

iterator operator++ (int i)
{
  iterator temp = (*this);
  ++(*this);
  return temp;
}; 

Upvotes: 6

Nicol Bolas
Nicol Bolas

Reputation: 473302

While I could use the -- operator, this would decrement this final iterator on each cycle.

No, it would not. It would get a copy of the end iterator and decrement it. That's all. It would not change the end iterator stored in the list.

Your main issue should be verifying that the list is not empty, thus ensuring that --it->end() exists.

Upvotes: 4

Related Questions