Determine order of set iterators - c++

I have two iterators (say it1 and it2) into the same std::set<int>. They are obtained through lower_bound and upper_bound, therefore they are not safe to dereference (they could equal end()).
Is there an easy and safe way of telling which one goes first?
I could call std::distance(it1, it2) and std::distance(it2, it1), but that does not seem to help since if it1 != it2 then one of the calls is UB. I could test *it1 < *it2, but only if no iterator points to the end(). Finally, I could first test for the end iterator and then do the above comparison on values.
Is there an elegant solution based purely on iterators and not involving the values, i.e., dereferencing? I am willing to use up to c++14 and maybe boost.
EDIT (in reponse to comments):
I use a set because I want fast lookup and insertion, in particular much faster than linear complexity. A sorted vector would be a possible alternative, it would trivially solve the problem, but insertion and removal are linear time operations.

In my opinion, the best way is to fix your code logic and have [it1,it2) always be a valid range; if this turns out not possible (but how can it be ?), you may use something like
// O(N), forward iterators, it1, it2 should belong to range
template<class Iter>
bool precedes_or_is_equal( Iter it1, Iter it2, Iter end )
{
while( it1 != end && it1 != it2 ) ++it1;
return it1 == it2;
}

Related

Why no operator+ for std::list iterators?

I was about to write code like this:
std::list<whatevertype> mylist;
// ...
std::list<whatevertype>::iterator it;
for(it = mylist.begin(); it != mylist.end(); ++it) {
// ...
if(some condition)
mylist.erase(it);
}
But I realized, this code is wrong: mylist.erase(x) will invalidate the iterator it, so the ++it is likely to fail.
So I tried changing it to
std::list<whatevertype>::iterator it;
std::list<whatevertype>::iterator nextit;
for(it = mylist.begin(); it != mylist.end(); it = nextit) {
// ...
nextit = it + 1;
if(some condition)
mylist.erase(it);
}
But, to my surprise, this failed: evidently operator+ is not defined for std::list iterators.
I've since found this other question and learned that the standard idiom for deleting "out from under" an iterator is more like
for(it = mylist.begin(); it != mylist.end(); ) {
if(some condition)
it = mylist.erase(it);
else ++it;
}
I believe I could also get away with
for(it = mylist.begin(); it != mylist.end(); ) {
// ...
std::list<whatevertype>::iterator previt = it;
++it;
if(some condition)
mylist.erase(previt);
}
But my question is, is there a reason that operator+ is not defined for these iterators?
One rule they had with the std iterators and collection was to make expensive things verbose.
On a list iterator, it+50 takes O(50) time. On a vector iterator, it+50 takes O(1) time. So they implemented + on vector iterators (and other random access iterators) but not on list iterators (and other weaker iterators).
std::next and std::advance and std::prev can solve your problem easier:
auto previt = std::prev(it);
or
auto nextit = std::next(it);
these also take a count, but because they are an explicit function call it was decided that them being expensive is acceptable.
Among other things, you can search for calls to std::next and std::prev and get iterator manipulation; + is heavily overloaded and finding the expensive calls is hard.
Note that std::basic_string doesn't follow the same conventions as other std containers.
It isn't that + is missing for all iterators. It is missing for std::list iterators.
That's because a list iterator is incredibly inefficient at random access. Therefore, making random access easy is a bad idea.
You can use std::advance. It makes it more evident that you are moving across the list one element at a time.
std::list uses a BidirectionalIterator which only defines increment and decrement. As std::list is a linked list the implementation of the iterator can only move one node at a time.
The interface is designed to make sure you know that moving by more than one element isn't a simple operation like it is with other iterators like a RandomAccessIterator returned from a std::vector.
see http://en.cppreference.com/w/cpp/concept/Iterator for a definition of the different iterator types.

Point to previous value without decrementing pointer

This is a pretty simple question.
Basically, say I have two iterators, it1 and it2. Given a value for it1, I want to define it2 to point to a location one address earlier. It would be cool if I could do it in one line, like:
vector<int>::iterator it2 = --it1;
However, this simultaneously decrements it1, so I have to re-increment it1.
vector<int>::iterator it2 = --it1;
++it1;
If these two lines are involved in a performance-intensive loop, I will have lots of it1 going back and forth for no good reason, just to define it2. On the other hand, if I do:
vector<int>::iterator it2 = it1;
--it2;
This is also slightly less than optimal as it involves two steps. Is there a way to do it in one?
You're looking for std::prev:
vector<int>::iterator it2 = std::prev(it1);
For vector's iterator, pointers and random access iterator in general, you can also use operator -:
vector<int>::iterator it2 = it - 1;

Peeking the next element in STL container

Is it possible to peek next element in a container which the iterator currently points to without changing the iterator?
For example in std::set,
int myArray[]= {1,2,3,4};
set <int> mySet(myArray, myArray+4);
set <int>::iterator iter = mySet.begin();
//peek the next element in set without changing iterator.
mySet.erase(iter); //erase the element if next element is n+1
C++0x adds a handy utility function, std::next, that copies an iterator, advances it, and returns the advanced iterator. You can easily write your own std::next implementation:
#include <iterator>
template <typename ForwardIt>
ForwardIt next(ForwardIt it,
typename std::iterator_traits<ForwardIt>::difference_type n = 1)
{
std::advance(it, n);
return it;
}
You can use this in your example like so:
if (iter != mySet.end() && next(iter) != mySet.end() && *next(iter) == *iter + 1)
mySet.erase(iter);
Not with iterators in general. An iterator isn't guaranteed to be able to operate non-destructively. The classic example is an Input Iterator that actually represents an underlying input stream.
There's something that works for this kind of iterator, though. A Forward Iterator doesn't invalidate previous copies of itself by the act of moving forward through the collection. Most iterators (including those for STL collections) are at least Forward Iterators, if not a more functional version- only Input Iterators or Output Iterators are more restricted. So you can simply make a copy of your iterator, increment the copy and check that, then go back to your original iterator.
So your peek code:
set <int>::iterator dupe = iter;
++dupe;
// (do stuff with dupe)
set <int>::iterator iter2 = iter;
++iter2;
int peekedValue = *iter2;
You can always make a copy of the iterator and advance the copy:
set <int>::iterator iter = mySet.begin();
set <int>::iterator iterCopy = iter;
iterCopy++;
if (*iterCopy == something)
mySet.erase(iter);
But beware that iterCopy may no longer be valid once you erase iter.
for sequence containers (vector, deque, and list) you can call front which will give you a peek (more info on the lower part of this link).
This will not work for std::set as its nature does not allow for the [] operator, but for containers that do, you can do:
std::vector<int> v;
v.push_back(3);
v.push_back(4);
std::vector<int>::iterator it = v.begin();
std::cout << v[it - v.begin() + 1];
But this could be dangerous if it points to the last element in the container; but the same applies to the solution above. E.g. you'll have to make checks in both cases.

How to check whether STL iterator points at anything? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C++ Best way to check if an iterator is valid
I want to do something like this:
std::vector<int>::iterator it;
// /cut/ search for something in vector and point iterator at it.
if(!it) //check whether found
do_something();
But there is no operator! for iterators. How can I check whether iterator points at anything?
You can't. The usual idiom is to use the container's end iterator as a 'not found' marker. This is what std::find returns.
std::vector<int>::iterator i = std::find(v.begin(), v.end(), 13);
if (i != v.end())
{
// ...
}
The only thing you can do with an unassigned iterator is assign a value to it.
Though the iterators are considered as general form of pointers, they are not exactly the pointers. The standard defines Past-the-end iterator to indicate the search failure in containers. Hence, it is not recommended to check the iterators for NULL
Past-the-end values are nonsingular and nondereferenceable.
if(it != aVector.end()) //past-the-end iterator
do_something();
If you want to use iterator in a loop, the safest way to use it is in this fashion:
for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it)
{
do_smth();
}
I believe this should generally give you a good test:
if (iterator._Mycont == &MyContainer)
{
Probably a valid iterator!
}
You could do tests to make sure that the iterator does not equal the end...
iterator != MyContainer.end()
and:
iterator >= MyContainer.begin()

How do you iterate backwards through an STL list?

I'm writing some cross-platform code between Windows and Mac.
If list::end() "returns an iterator that addresses the location succeeding the last element in a list" and can be checked when traversing a list forward, what is the best way to traverse backwards?
This code workson the Mac but not on Windows (can't decrement beyond first element):
list<DVFGfxObj*>::iterator iter = m_Objs.end();
for (iter--; iter!=m_Objs.end(); iter--)// By accident discovered that the iterator is circular ?
{
}
this works on Windows:
list<DVFGfxObj*>::iterator iter = m_Objs.end();
do{
iter--;
} while (*iter != *m_Objs.begin());
Is there another way to traverse backward that could be implemented in a for loop?
Use reverse_iterator instead of iterator.
Use rbegin() & rend() instead of begin() & end().
Another possibility, if you like using the BOOST_FOREACH macro is to use the BOOST_REVERSE_FOREACH macro introduced in Boost 1.36.0.
The best/easiest way to reverse iterate a list is (as already stated) to use reverse iterators rbegin/rend.
However, I did want to mention that reverse iterators are implemented storing the "current" iterator position off-by-one (at least on the GNU implementation of the standard library).
This is done to simplify the implementation, in order for the range in reverse to have the same semantics as a range forward [begin, end) and [rbegin, rend)
What this means is that dereferencing an iterator involves creating a new temporary, and then decrementing it, each and every time:
reference
operator*() const
{
_Iterator __tmp = current;
return *--__tmp;
}
Thus, dereferencing a reverse_iterator is slower than an normal iterator.
However, You can instead use the regular bidirectional iterators to simulate reverse iteration yourself, avoiding this overhead:
for ( iterator current = end() ; current != begin() ; /* Do nothing */ )
{
--current; // Unfortunately, you now need this here
/* Do work */
cout << *current << endl;
}
Testing showed this solution to be ~5 times faster for each dereference used in the body of the loop.
Note: Testing was not done with the code above, as that std::cout would have been the bottleneck.
Also Note: the 'wall clock time' difference was ~5 seconds with a std::list size of 10 million elements. So, realistically, unless the size of your data is that large, just stick to rbegin() rend()!
You probably want the reverse iterators. From memory:
list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
for( ; iter != m_Objs.rend(); ++iter)
{
}
As already mentioned by Ferruccio, use reverse_iterator:
for (std::list<int>::reverse_iterator i = s.rbegin(); i != s.rend(); ++i)
This should work:
list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
for (; iter!= m_Objs.rend(); iter++)
{
}