1

I know this is very stupid question, but I wanted to clarify this.

Let's say I have one string vector looks like,

vector<int> vnTemp; // suppose this vector has {1,2,3,4,5}
vector<int>::iterator vn_it;

//Now, I want to print out only 1 to 4.
for(int i=0; i<4; ++i)
    cout << vnTemp[i] << endl;

Quite simple. but what should I do when I want to print out the equivalent result by using iterator? for exmample,

// .. continuing from the code above
for(vn_it = vnTemp.begin(); vn_it != vnTemp.end()-1; ++vn_it)
    cout << *it << endl;

Of course, vnTemp.end()-1 will lead an error since it's pointer.

what is the equivalent for loop in this case? and is there any performance difference when they both are compiled in optimized(-o) mode?


Edit:

I've just realized this actually works with vector. The problem happened when I was using boost::tokenizer the code is like this:

typedef boost::tokenizer<boost::char_separator<char> > tokenizer;

boost::char_separator<char> sep("_");
tokenizer::iterator tok_it;

tokenizer tokens(strLine, sep); //strLine is some string line
for(tok_it=tokens.begin(); tok_it != tokens.end(); ++tok_it){
... }

This was the original code, and error occurs when I try to say tokens.end()-1 in a for loop.

Is there any way that I can fix this problem? Sorry about the ambiguity.

devEvan
  • 361
  • 4
  • 16
  • 4
    That should work. The iterator implements `operator-`. Also, iterators are not just pointers. For example, `std::list` keeps its elements in non-contiguous memory. – chris May 08 '12 at 22:23
  • 2
    I would say that your problem is somewhat contrived. Real-world problems usually concern either an entire container, or otherwise some sort of selection that *can* be written very nicely with iterators (like `remove_if` or `find`), and iterators are indeed a more appropriate way to talk about collections than indexes. – Kerrek SB May 08 '12 at 22:28
  • Forward iterators support increment, bidirectional iterators support increment and decrement, and random access iterators support both as well as additional arithmetic like `begin()+4` ... vector uses random access iterators so you have options. I believe `boost::tokenizer` defaults to `std::string::const_iterator` – AJG85 May 08 '12 at 22:42
  • `boost::token_iterator` is a forward_iterator, that's why you have the problem. – Jesse Good May 08 '12 at 22:43
  • Oh I see. Is it possible to set a step size of iteration in this case? – devEvan May 08 '12 at 22:50

1 Answers1

3

boost::tokenizer only provides a forward iterator; which means that you can't subtract from an tokenizer iterator. If you want your loop to avoid processing the last token you are going to have to "look-ahead" before you process the token you are on. Something like this

tokenizer tokens(strLine, sep); //strLine is some string line
tok_it = tokens.begin();
if(tok_it!=tokens.end())
{
    ++tok_it;
    for(auto last_tok = tokens.begin(); tok_it != tokens.end(); ++tok_it){
        // Process last_tok here
        . . .
       last_tok = tok_it;    
    }
}

Edit: An alternative approach would be to copy the tokens into a container with more flexible iterators:

std::vector<std::string> resultingTokens(tokens.begin(), tokens.end());
for(auto tok=resultingTokens.begin(); tok!=resultingTokens.end()-1; ++tok)
{
    // whatever
}
zdan
  • 28,667
  • 7
  • 60
  • 71
  • This answer is essentially correct, but it needs a bit of cleanup: `+1` still won't work on forward iterators - you can only use the increment operator: `++`. Also, it's more correct to say that *at best* `boost::tokenizer` will produce forward iterators. For example, if the underlying container is a stream, `boost::tokenizer` will produce input iterators which are slightly more limited. – Michael Burr May 08 '12 at 23:21
  • Ooops. Right you are. Thanks @Michael – zdan May 08 '12 at 23:49
  • What if I want to set a step size? say 2? Can I write like, `++(++tok_it)`? – devEvan May 08 '12 at 23:59
  • @devEvan: you should probably play around with a working example if you want to understand what you can and can't do (and why). `++(++tok_it)` should work, but it makes the code complex. And it's sensitive to the number of tokens (you really need to check for hitting `end()` after each increment). If you're going to iterate over the tokens with these kinds of odd steps and boundaries, it might make sense to just copy the tokens into some other container (like a `deque`) that you can use random access iterators on - the code will likely be much cleaner. – Michael Burr May 09 '12 at 00:35
  • Your first approach increments the iterator returned by `begin()` before comparing it with `end()`, which will have undefined behaviour when there are no tokens - easily fixed with an `if` statement controlling the `for` loop. – Tony Delroy May 09 '12 at 00:53