2

I'm writing a code that divides a vector in to equal parts using simple arithmetic on the iterators. My logic might not be very good (although I'm fairly certain this is correct) which might mean I create an invalid iterator past end():

//selectWords is a vector<string>
//No is an unsigned integer.

unsigned wordNo = selectWords.size()/No; 
unsigned remainder = selectWords.size()%No; 

for(auto i = 0; i < No; ++i){
    unsigned extra = remainder ? 1 : 0;

    auto b = selectedWords.begin()+(i*wordNo);
    auto e = selectedWords.begin() + ((i+1)*wordNo) + extra;

    if(e > selectedWords.end()) throw logic_error("Iterator e in GridList::createGrids goes past the end of selectWords.") //shouldn't happen unless I've made a mistake.

    ...
    //do stuff
    ...

    if(remainder > 0) --remainder;

}

The part I'm curious about is e > selectedWords.end(). If e is already past end(), then it is an invalid iterator and I'm not sure whether there is any guarantee that the comparison operator provides defined behaviour. Indeed looking at Ben Voigt's post in this thread and 24.2.7 of N3337 it is clear that using iterator arithmetic to create a past-the-end() iterator is meaningless or illegal. Yet the ability to check whether an iterator is past end() requires that operations on past-the-end() iterators provide defined behaviour.

If it is not valid to do this comparison, is there any other way? Or do I just have to make absolutely sure my logic before compiling is correct?

Community
  • 1
  • 1
SergeantPenguin
  • 843
  • 7
  • 16
  • 1
    If your iterator is end, and you add one to it; the implementation can return end() - meaning that your test of iterator > end will always be false. – UKMonkey Oct 14 '16 at 11:38
  • 1
    You can test `((i + 1) * wordNo) + extra > selectedWords.size()` instead. – Jarod42 Oct 14 '16 at 12:01

2 Answers2

0

Going past an end iterator (e.g. incrementing it) gives undefined behaviour.

If something can only exist as a result of undefined behaviour, it cannot exist as a result of executing any operation in code that has well-defined behaviour. Logically, that means there need be no defined way of testing for it.

Peter
  • 35,646
  • 4
  • 32
  • 74
0

You can simply correct your code to get the right end position:

size_t No = 3;
std::vector<int> selectWords = {1,2,3,4,5,6,7,8,9,10};
size_t wordNo = (selectWords.size() + No - 1)/No; 
size_t remainder = selectWords.size()%No; 

for(size_t i = 0; i < No; ++i){
    size_t start_index = i * wordNo;
    size_t end_index = std::min(selectWords.size(), start_index + wordNo);
    auto b = selectWords.begin() + start_index;
    auto e = selectWords.begin() + end_index;
    for (auto it = b; it != e; ++it)
        std::cout << *it;
 //....
}

Test it

Evgeniy
  • 2,481
  • 14
  • 24