When I'd like to traverse a map in C++, we may use the following technique:
for (auto i = m.begin(); i != m.end(); i++)
{ ... ... }
Why cannot we use the following instead:
for (auto i = m.begin(); i < m.end(); i++)
{ ... ... }
My guess is because the elements in an associative container are not stored in sequential order like sequential containers, is that right?
The comparison operator < requires random access iterators.
map only provides Bidirectional iterators. The reason is that you cannot say with just such an iterator if another iterator is before or after in constant time (yes, they are not one after the other in memory).
As != is valid for all types of iterators, use it instead of the < version. It's portable if you change the container type.
Related
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.
Is there a simple way to compare an iterator with an int?
I have a loop like this:
for (std::vector<mystruct>::const_iterator it = vec->begin(); it != vec->end(); ++it)
Instead of looping over the entire vector, I would like to just loop over the first 3 elements. However, the following does not compile:
for (std::vector<mystruct>::const_iterator it = vec->begin(); it < 3; ++it)
It there a good way to achieve the same effect?
since it's a vector, why not just access its position directly ?
if (vec->size() > 0)
{
for (int i =0; i<3 && i< vec->size(); i++)
{
// vec[i] can be accessed directly
//do stuff
}
}
std::next(vec->begin(), 3); will be the the iterator 3 places after the first, and so you can compare to it:
for (std::vector<mystruct>::const_iterator it = vec->begin(); it != std::next(vec->begin(), 3); ++it)
Your vector will need to have at least 3 elements inside it though.
I'd want to be careful, because you can easily run into fencepost bugs.
This works on random access containers (like vector and array), but doesn't do ADL on begin because I'm lazy:
template<typename Container>
auto nth_element( Container&& c, std::size_t n )->decltype( std::begin(c) )
{
auto retval = std::begin(c);
std::size_t size = std::end(c) - retval;
retval += std::min( size, n );
return retval;
}
It returns std::end(c) if n is too big.
So you get:
for( auto it = vec->cbegin(); it != nth_element(vec, 3); ++it) {
// code
}
which deals with vectors whose size is less than 3 gracefully.
The basic core of this is that on random access iterators, the difference of iterators is ptrdiff_t -- an integral type -- and you can add integral types to iterators to move around. I just threw in a helper function, because you should only do non-trivial pointer arithmetic (and arithmetic on iterators is pointer arithmetic) in isolated functions if you can help it.
Supporting non-random access iterators is a matter of doing some traits checks. I wouldn't worry about that unless you really need it.
Note that this answer depends on some C++11 features, but no obscure ones. You'll need to #include <iterator> for std::begin and std::end and maybe <algorithm> for std::min.
Sure, you can simply go three elements past the beginning.
for (std::vector<mystruct>::const_iterator it = vec->cbegin(); it != vec->cbegin() + 3; ++it)
However, that might be error prone since you might try to access beyond the end in the case that the vector is fewer than 3 elements. I think you'd get an exception when that happens but you could prevent it by:
for(std::vector<mystruct>::const_iterator it = vec->cbegin(); it != vec->cend() && it != vec->cbegin() + 3; ++it)
Note the use of cbegin() and cend() since you asked for a const_iterator, although these are only available in c++11. You could just as easily use begin() and end() with your const_iterator.
This question already has answers here:
Why use iterators instead of array indices?
(27 answers)
Closed 10 years ago.
Why should I use iterators?
For example if I have code like this:
for (int i = 0; i < vec.size(); i++)
cout << vec[i];
what would be the advantage of writing
for (vector<int>::iterator it != vec.begin(); it != n.end(); ++it)
cout << *it;
Also, why is writing i < vec.size() and i++ more common in the first example and it != begin() and ++it more common in the second example? What is the difference how you increment it and why not always use an equal sign?
I understand iterators can be useful in C++11 range-based for loops and some STD algorithms, but why should I do it in normal code, since it is more verbose?
Well not all containers have random access so you can do lookup on an index, so to normalize the interface iterators are fairly useful. Consider std::list. It doesn't support random access via a [] operator.
Taking this into account, to work across many heterogeneous types of containers, many STL functions like std::copy take iterators.
The point is that iterators allow you to iterate over anything that supports iterators in a generic fashion.
As for it being more verbose, the extra verbosity isn't horrible (and your example could be slightly improved using auto or using the C++11 range-based for loop) but that's really a stylistic issue.
Lets say we have this code:
typedef std::vector<std::string> strings;
strings strs;
for( strings::const_iterator it = strs.begin(); it != strs.end(); ++it ) {
}
And later for watever reason we decide to switch to std::list. So we just replace typedef and code:
typedef std::list<std::string> strings;
strings strs;
for( strings::const_iterator it = strs.begin(); it != strs.end(); ++it ) {
}
Will work as before. But code with index variable will fail. Imagine what if you need to write a template code.
The tl;dr is that iterators work better in a general case for different kinds of objects (when for example the size() method may be slow).
If you want to read more about it:
Why use iterators instead of array indices?
Iterators.. why use them?
I thought the idea of the iterator object was that you can apply it similarly to the C++ container classes. When I try to iterate through a list object, however, I tried using
for(list<int>::iterator it = obj.begin(); it < obj.end(); it++){
// some code
}
And I got an error. Why doesn't this work? Why would it work for vector::iterator? Is it just because of the implementation of list being bi-directional linked lists? I thought the iterator object abstracts that notion of moving through containers, thereby allowing it to operationally be the same, whether for vectors or lists.
I'd really appreciate a clarification.
This does not work because, unlike std::vector iterators, std::list iterators are not random-access - they are sequential. You need to use != on them:
for(list<int>::iterator it = obj.begin(); it != obj.end(); it++)
In general, it's a good idea to use "not equals" on all iterators when you are looking to cover the entire range, even when these iterators allow comparisons for < and >. There is also an argument in favor of using != in your regular for loops, too, because it gives you the strongest postcondition.
You have to compare with != as list iterators are scattered throughout all memory in random order.
Use: for(list<int>::iterator it = obj.begin(); it != obj.end(); it++)
That's because list does not support random access iterators, but only forward iterators. Therefore, operator < is not defined for iterators of a list. You have to use operator != for inequality comparisons.
Operator arithmetic, including ordering-comparison operators (like <), is only defined for random access iterators. If you change the code to use !=, it will work (assuming obj is a list<int>):
for(list<int>::iterator it = obj.begin(); it != obj.end(); it++){
// some code
}
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.