3

I am writing a wrapper class which looks like this:

class Wrapper{
private:
    std::list<People> men;
    std::list<People> woman;

    /**
        some bizzar logics
    **/
public:
    std::list<People>::iterator getMeTheNextOne(){};
}

The problem is, sometime, I need to return an empty (or NULL) iterator, saying that there is no more 'suitable' people in either list any more. If I simply return men.end() or women.end(), is the user gonna catch this?

Imaging the user have following code:

Wrapper wo;
std::list<People>::iterator it = wo.getMeTheNextPeople();
if(it == /*what should I put here? i cannot access the list members of the Wrapper*/){
// do something here
}
Robert
  • 10,403
  • 14
  • 67
  • 117
James Bond
  • 7,533
  • 19
  • 50
  • 64
  • 2
    There are no "null" iterators in C++. The value usually used to indicate e.g. "not found" is to return `end()`. – Some programmer dude Apr 17 '14 at 14:21
  • 1
    Add another iterator to hold the value of either men.end() or women.end(). When it is equal to that value, it is the end. That would fit with normal STL thinking. – cup Apr 17 '14 at 14:22
  • @cup: Wouldn't work: you can't compare `wo.getMeTheNextPeople()` to `men.end` if the next person was in fact a women and vice versa. – MSalters Apr 17 '14 at 14:51
  • @MSlaters The end value could be set when the iterations start. It is a function so it can return anything the coder wants. Having seen the answers, I think Marius has a better solution. – cup Apr 17 '14 at 15:43

5 Answers5

2

Returning a list iterator, when the user doesn't have access to the list which the iterator is coming from, is weird and ugly. Why not return a pointer to a People instead, which can be NULL?

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • Because it can't be incremented/etc? – Puppy Apr 17 '14 at 14:28
  • 3
    It can't (safely) be incremented anyway. The fact that he's returning elements from one of two lists suggests that the user has no way of knowing which list the next element should be drawn from. – Sneftel Apr 17 '14 at 14:31
  • Depends on what else he provides- for example, if he provides a men_end and a women_end then there's no problem. – Puppy Apr 17 '14 at 14:38
  • @DeadMG Sure, maybe the purpose of the function is to provide some suffix of one of the two lists. But it seems far more likely that the OP was thinking in terms of list iterators as element references simply because he was using lists. In the absence of more information about the use case, I'm going with Occam's Razor. – Sneftel Apr 17 '14 at 14:47
  • 1
    @DeadMG: Even if you provided both, the caller still wouldn't know which of the two to use. – MSalters Apr 17 '14 at 14:49
1

It doesn't make sense to return an iterator that can be from different lists. There is no way to check whether the iterator is valid. The best way in your approach is to return a pointer to the actual object being stored and that can be null.

On the other hand, what you could do if you insist to return an iterator is having a method in Wrapper to check the validity of the iterator.

class Wrapper{
private:
    std::list<People> men;
    std::list<People> woman;

    /**
        some bizzar logics
    **/
public:
    std::list<People>::iterator getMeTheNextOne(){};

    bool isValid(std::list<People>::iterator const & it) const 
    {
       return it != men.end() || it != women.end();
    }
};

That you could use like this:

Wrapper wo;
std::list<People>::iterator it = wo.getMeTheNextPeople();
if(wo.isValid(it))
{
   // do something here
}
Marius Bancila
  • 16,053
  • 9
  • 49
  • 91
1

Iterators can never be null. If an iterator does not point to anything, it's value is end(). I think it's OK for you to return end(). Usually the user uses an iterator to iterate over something, and when they iterate it is their responsibility to check whether they have reached the end or not, and the only way to check is to compare the iterator's value with end().

santahopar
  • 2,933
  • 2
  • 29
  • 50
0

Return the standard "beyond the range iterator": wo.end();

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
0

Typically the solution would be to have an accessor to the beginning and end iterator of the container, so adding a

class Wrapper {
public:
    std::list<People>::iterator end();
};

Would allow you do write the following:

Wrapper wo;
std::list<People>::iterator it = wo.getMeTheNextPeople();
if(it == wo.end()){
// do something here
}

However in this situation where you have two separate lists you may need to add an endOfMen and an endOfWomen, or combine them in a single list, depending on which best solves your problem.

Tim Beaudet
  • 791
  • 7
  • 16