Say I have an iterator it which is pointing to some element of map.
Also I have another iterator it1 , and I want to do something like this
it1 = it + 1;
How can we achieve this in C++ as above statement gives error in C++.
In C++11, you say auto it1 = std::next(it, 1);.
Prior to that, you have to say something like:
std::map<K, T>::iterator it1 = it;
std::advance(it1, 1);
Don't forget to #include <iterator>.
Related
In below code.
int main() {
list<int> m_list;
m_list.push_back(1);
list<int>::iterator it1 = (--m_list.end()); // it works, *it1 return 1;
list<int>::iterator it2 = (m_list.end() - 1); // compile issue?
}
Anybody explain why in list (m_list.end() - 1) has compile issue? and why (--m_list.end()) is OK?
If we change to others, vector, string. both cases do work.
int main() {
vector<int> m_vector;
m_vector.push_back(1);
vector<int>::iterator it1 = (--m_vector.end()); // both work
vector<int>::iterator it2 = (m_vector.end() - 1); // both work
}
The reason behind this is that list::end() returns a bidirectional iterator which does not support such operation.
Source:
http://www.cplusplus.com/reference/iterator/BidirectionalIterator/
On the other hand, vector::end() and string::end() returns a random access iterator which supports such operation.
http://www.cplusplus.com/reference/iterator/RandomAccessIterator/
Edit:
If you really want to accomplish the task, use std::prev() function
list<int>::iterator it2 = (std::prev(m_list.end(), 1));
As suggested by Pete Becker, "The second argument to std::prev has a default of 1"
So, you may do this also:
list<int>::iterator it2 = (std::prev(m_list.end()));
Anybody explain why in list (m_list.end() - 1) has compile issue?
Because list iterator doesn't support random access. Only random access iterators are guaranteed to support operator- (and operator+).
and why (--m_list.end()) is OK?
Because bidirectional iterators support operator-- (and operator++). List iterator is bidirectional.
If we change to others, vector, string. both cases do work.
Both vector and string have random access iterators.
I have two very similar bits of code; this:
std::vector<int> fail{0};
fail.reserve(2);
std::vector<int>::iterator it1 = fail.begin(), it2 = fail.begin() + 1;
fail.push_back(0);
it1 == it2;
which throws a "vector iterators incompatible" exception and this:
std::vector<int> fail{0, 0};
fail.reserve(3);
std::vector<int>::iterator it1 = fail.begin(), it2 = fail.begin() + 1;
fail.push_back(0);
it1 == it2;
which doesn't. It seems to be due to the it2 being the end of the vector in the first example but not in the second, but I'd just like to get a full clarification for why the first throws but the second doesn't.
For reference I am using MSVC.
std::vector::push_back always invalidates the past-the-end iterator, so in the first case it2. This happens regardless of resizing.
All other iterators stay intact if the vector does not reallocate, that's why they second snippet is fine.
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;
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.
For example, it this expression valid in semantic?
container.begin() == container.begin();
Yes, so long as neither iterator has been invalidated.
For example, the following would not be valid:
std::deque<int> d;
std::deque<int> begin1 = d.begin();
d.push_front(42); // invalidates begin1!
std::deque<int> begin2 = d.begin();
assert(begin1 == begin2); // wrong; you can't use begin1 anymore.
Yes, begin() will return the same iterator given a container instance, unless you change the container in some way (end() has this property as well). For example, std::vector::push_back() may cause the array to be reallocated to accommodate new elements.