How can I get the next/previous iterator in a loop without modifying the current iterator? I cannot use either operator++ or operator-- as that would disturb the actual iteration of the loop.
If I were iterating over array indices i, I could say A[i+1] = A[i] without actually modifying i. How can I do the equivalent for iterators?
If C++11, use std::next():
auto it = ...;
auto next_it = std::next(it); // doesn't modify 'it'
If C++03, write your own std::next() based off of std::advance():
template<class ForwardIt>
ForwardIt next(ForwardIt it,
typename std::iterator_traits<ForwardIt>::difference_type n = 1)
{
std::advance(it, n);
return it;
}
Note that if your iterator is a random access iterator, you could do the same thing you do for your array index. That is:
auto next_it = it + 1;
I could say A[i+1] = A[i] without actually modifying i
The syntax is the same for random-access iterators (like that of std::vector):
iterator + 1 // next
and
iterator - 1 // previous
For a non-random-access iterator (or if you want something generic) use std::next and std::prev:
std::next(iterator) // next
and
std::prev(iterator) // previous
Related
I read the description for C++ upper_bound() function and lower_bound() function. It is interesting for me that upper_bound() only returns the first iterator with value > val (or return last iterator in the range [first, last) if val not found).
The implementation is different from lower_bound(), while it returns the first iterator NOT SMALLER than val, so it allows the return pointer equivalent to val.
I am just curious to know what is the purpose to design upper_bound() in this way that upper_bound() must NOT return an iterator with value equivalent to val?
For example:
vector<int> a = {1, 2, 3, 4};
auto i = lower_bound(a.begin(), a.end(), 2); // i is iterator at 2;
auto j = upper_bound(a.begin(), a.end(), 2); // j is iterator at 3;
http://www.cplusplus.com/reference/algorithm/lower_bound/
In C++, iterators usually work in pairs. The first iterator points to the first element to consider, and the last iterator points to one-past-the-last element to consider. This is to make looping easy:
for(it cur=first; cur!=last; cur++)
As such, lower_bound and upper bound together, form a "range of all elements equal to the item you searched for. The same range that std::equal_range returns.
upper_bound is useful in cases like std::multimap where values are stored in sorted order. It lets you traverse within a range where starting position can be decided by lower_bound, and end-before-this-position is decided by upper_bound.
for (iter_type it = myMap.lower_bound("key_val"); it != myMap.upper_bound("key_val"); it++)
This for loop would traverse till the point key == key_val.
I'm (forward) iterating over a std::map and would like to find if the iterator points to the second last element. I can't seem to find how to do that anywhere.
I've got:
bool
isSecondLastFile(const TDateFileInfoMap::const_iterator &tsFile)
{
TDateFileInfoMap::reverse_iterator secondLastIt = mFileInfoMap.rbegin() + 1;
return (tsFile == secondLastIt);
}
Where TDateFileInfoMap is std::map
I'm getting:
error: no match for ‘operator==’ in ‘tsFile == secondLastIt’
/usr/lib/gcc/i686-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_tree.h:287: note: candidates are: bool std::_Rb_tree_const_iterator<_Tp>::operator==(const std::_Rb_tree_const_iterator<_Tp>&) const [with _Tp = std::pair<const long int, TFileInfo>]
Does that mean I can't compare the forward and reverse iterator?
How do I figure out if the forward iterator is pointing at the second last element?
std::map's iterator type is BidirectionalIterator. Just decrement the end iterator twice--first to get the last element since m.end() returns an iterator at the after the end position, and then again to get the second-last element:
auto penultimate = std::prev(m.end(), 2);
Then you can simply check for equality with the resultant iterator:
auto it = m.begin();
it == penultimate;
see it live on Coliru
Naturally, you should check that the map has two elements first if it's not guaranteed by other logic in your program.
Does that mean I can't compare the forward and reverse iterator?
Yes you can't compare them directly.
You can use base() to get the underlying base iterator.
Returns the underlying base iterator. That is
std::reverse_iterator(it).base() == it.
The base iterator refers to the element that is next (from the
std::reverse_iterator::iterator_type perspective) to the element the
reverse_iterator is currently pointing to. That is &*(rit.base() - 1) == &*rit.
e.g.
return (tsFile == (++secondLastIt).base());
BTW: mFileInfoMap.rbegin() + 1 won't compile since the iterator of std::map is not RandomAccessIterator. You might write:
TDateFileInfoMap::reverse_iterator secondLastIt = mFileInfoMap.rbegin();
++secondLastIt;
Note that we're not checking whether the map is empty or has only one element.
A simple solution for forward iterators:
template <typename ForwardIterator>
inline bool isNthLast(std::size_t n, ForwardIterator pos, ForwardIterator last) {
for( ;; --n, ++pos) {
if(n == 0)
return (pos == last);
if(pos == last)
return false;
}
}
bool isSecondLastFile(TDateFileInfoMap::const_iterator sFile) {
return isNthLast(2, sFile, mFileInfoMap.end());
}
Let's say you have a set with name s.
s= {s1,s2,...,sN-1, sN}
Now to iterate from s1.. to sN-1 (which is second last element) we will use STL functions, s.begin() and s.end().
end = s.end(); //end points to end
end--// end points to sN
Now in the for loop when itr (starts from the beginning of set) becomes equal to sN the loop will break, and you will get s1,s2,..sN-1 inside the loop.
map<int,int> s;
// to iterate till fixed range in map
auto end =s.end();
end--; // end to second last;
for(auto itr = s.begin(); itr!=end;itr++){
// do your operation
}
I have a list like below
typedef std::list<std::string> SegmentValue;
then in a iteration I need check if this is last iteration.
for(Field::SegmentValue::const_iterator it = m_segmentValue.begin();It !=
m_segmentValue.end();It++){
if((segIt + 1) == m_segmentValue.end())//last iteration
...
}
but I get error in compile that:
error C2678: binary '+' : no operator found which takes a left-hand operand of type 'std::list<_Ty>::_Const_iterator<_Secure_validation>'
how I can check if this is last itration?
You can't use binary + and - operators with std::list iterators. std::list iterators are bidirectional iterators, but they are not random access iterators, meaning that you can't shift them by an arbitrary constant value.
Use unary ++ and -- instead
Field::SegmentValue::const_iterator it_last = m_segmentValue.end();
--it_last;
Now it_last is the last element iterator. Just make sure it remains valid. If you are not making any iterator-invalidating modifications to your container, you can pre-compute it_last and use it in the cycle. Otherwise, you'll have to re-compute it as necessary.
In fact, in generic algorithms it is always a good idea to prefer using -- and ++ with iterators whenever possible (instead of binary + 1 and - 1), since it reduces your algorithm's requirements: binary + and - require random access iterators, while ++ and -- work with bidirectional ones.
Use std::next:
if (std::next(segIt) == m_segmentValue.end()) ...
If you're using C++03, you can easily write next yourself:
template<typename T> T next(T it, typename std::iterator_traits<T>::difference_type n = 1) {
std::advance(it, n);
return it;
}
Something like this perhaps:
Field::SegmentValue::const_iterator last = m_segmentValue.end()
--last;
for(Field::SegmentValue::const_iterator it = m_segmentValue.begin();
It != m_segmentValue.end();
It++) {
if(It == last) {
// last iteration
}
}
You can only do arithmetic with Random Access Iterators. std::list's iterators are Bidirectional.
See here for what you can and cannot do with iterators of various categories.
Try this:
Field::SegmentValue::const_iterator next = it; ++next;
// or in C++11:
// Field::SegmentValue::const_iterator next = std::next( it );
if( next == m_segmentValue.end()) //last iteration
List iterators are Bidirectional, not RandomAccess so they don't support operator+.
std::list iterator are not random access, they are bidirectional. The operator+ is not supported. You need to use std::vector to do something like that.
How about:
if ( &*it == &*(m_segmentValue.rbegin()))
i.e, comparing the addresses of the segments.
There have been a few questions regarding this issue before; my understanding is that calling std::vector::erase will only invalidate iterators which are at a position after the erased element. However, after erasing an element, is the iterator at that position still valid (provided, of course, that it doesn't point to end() after the erase)?
My understanding of how a vector would be implemented seems to suggest that the iterator is definitely usable, but I'm not entirely sure if it could lead to undefined behavior.
As an example of what I'm talking about, the following code removes all odd integers from a vector. Does this code cause undefined behavior?
typedef std::vector<int> vectype;
vectype vec;
for (int i = 0; i < 100; ++i) vec.push_back(i);
vectype::iterator it = vec.begin();
while (it != vec.end()) {
if (*it % 2 == 1) vec.erase(it);
else ++it;
}
The code runs fine on my machine, but that doesn't convince me that it's valid.
after erasing an element, is the iterator at that position still valid
No; all of the iterators at or after the iterator(s) passed to erase are invalidated.
However, erase returns a new iterator that points to the element immediately after the element(s) that were erased (or to the end if there is no such element). You can use this iterator to resume iteration.
Note that this particular method of removing odd elements is quite inefficient: each time you remove an element, all of the elements after it have to be moved one position to the left in the vector (this is O(n2)). You can accomplish this task much more efficiently using the erase-remove idiom (O(n)). You can create an is_odd predicate:
bool is_odd(int x) { return (x % 2) == 1; }
Then this can be passed to remove_if:
vec.erase(std::remove_if(vec.begin(), vec.end(), is_odd), vec.end());
Or:
class CIsOdd
{
public:
bool operator()(const int& x) { return (x % 2) == 1; }
};
vec.erase(std::remove_if(vec.begin(), vec.end(), CIsOdd()), vec.end());
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.