Suppose I have a map named m and an iterator i for the map. Presently I am visualising a map iterator as an array index and I want to implement a code like the one given below:
for(auto i = m.begin(); i != m.end(); i++) {
auto l = i - 1; // error type 1
auto r = i + 1; // error type 1
while(l >= m.begin() && r < m.end()) { // error type 2
// ...
r++;
l--;
}
}
Now, I have got some questions which has been confusing me a bit.
For the error type 1 stated in the code, incrementing or decrementing the iterator value gives error, but the similar operation done within the loop (I mean to say i++) gives no error.
For error type 2, why does comparing two iterators (l >= m.begin()), straight up give error, but doing the similar operation in a loop does not give error?
And finally, how could I make this code work on the lines of how an array index works using this map? I hope you can understand what I am trying to implement.
Iterator for std::map is defined by standard as Bidirectional Iterator. This type of iterators can be incremented(operator ++) and decremented (operator --), but you can't perform mathematical operations on them (mostly because it would take O(n) time rather than O(1))
And again, for error 2, bidirectional iterator does not overload < operator (nor other variations), as it doesn't make sense to have compare operator with O(n) complexity. They are overloaded in random access iterators at the lowest.
To achieve what you wanted, your code can look like this:
#include <iterator> /for std::next() and std::prev()
for(auto i = m.begin; i != m.end(); ++i)
{
auto l = i;
auto r = i;
if (i != m.begin())
l = std::prev(i);
if (i != m.end())
r = std::next(i);
while (l != m.begin() && r != m.end())
{
//make sure you don't use r if it's equal to m.end()
--l;
++r;
}
Map is an associative container. The following containers are defined in the current revision of the C++ standard with associative container: set, map, multiset, multimap.
Associative Containers support bidirectional iterators. Bidirectional iterators are iterators that can be used to access the sequence of elements in a range in both directions (towards the end and towards the beginning). They are similar to forward iterators, except that they can move in the backward direction also, unlike the forward iterators, which can move only in forward direction.
Bidirectional iterators support following operations:
Is default-constructible, copy-constructible, copy-assignable and
destructible X a; X b(a); b = a;
Can be compared for equivalence using the equality/inequality
operators (meaningful when both iterator values iterate over the same
underlying sequence). a == b a != b
Can be dereferenced as an rvalue (if in a dereferenceable state).
*a a->m
For mutable iterators (non-constant iterators): Can be dereferenced
as an lvalue (if in a dereferenceable state). *a = t
Can be incremented (if in a dereferenceable state). The result is
either also dereferenceable or a past-the-end iterator. Two iterators
that compare equal, keep comparing equal after being both increased.
++a a++ *a++
Can be decremented (if a dereferenceable iterator value precedes it).
--a a-- *a--
So, + and - is not defined for it, which leads to the error1.
And so is >=, <=, >, < not defined for it, which leads to error2.
The problem is iterator doesn't work like array indices. The elements of map will be stored at different locations in memory and there is no guarantee that the between elements i and j in map, if i comes before j, then it will be stored in memory just before j. The values are not stored at consecutive locations in memory in case of map.
The ++ operator is overloaded for iterators and it will give the proper location of the next element.
Also, if you consider the points above, then comparing two iterators makes no sense because the fact that one iterator comes after another doesn't give us any important information regarding the corresponding values that will be accessed using these two iterators.
there are different iterator categories/concepts: Iterator, ForwardIterator, BidirectionalIterator, RandomAccessIterator and ContiguousIterator. They differ in available operations. Simple iterators only support step forward (operator ++), dereference (operator *) and inequality comparison (operator !=). This is the required minimum for range-based for loop. std::map::iterator is BidirectionalIterator - it doesn't support arithmetic or comparison operators.
Solving the x-y problem: I want to do "this", I wrote code that does "that"
So I presume you really do want to have access to the previous element in the map, and the next element. I presume you only want this when those 2 elements are "good"
You could recast your loop to cascade the knowledge of the 3 neighbouring iterators:
for(auto r = m.begin(), e= m.end(), i= e, l= e; r != e; l =i, i =r, r++)
{
if (l != e)
{
// All 3 iterators are good here
}
}
Related
I am trying to reverse a std::string:
void reverseString(vector<char>& s)
{
auto i = s.begin();
auto j = s.rbegin();
while (i != j)
{
char tmp = *i;
*i = *j;
*j = tmp;
i++;
j--;
}
}
However, this happened when I tried to compare iterators
ERROR:
Line 6: Char 17: error: invalid operands to binary expression
('__gnu_cxx::__normal_iterator<char *, std::vector<char, std::allocator<char> > >' and
'std::reverse_iterator<__gnu_cxx::__normal_iterator<char *, std::vector<char, std::allocator<char> > > >')
while(i != j) {
You have a few issues in your code:
Firstly i has the type std::vector<char>::iterator and the j
has the type std::vector<char>::reverse_iterator, which
are not same. Therefore you can not do
while(i != j)
That is the reason for the compiler error!
Secondly, a reverse iterator should be as like a normal iterator.
Meaning j--; will try to move one past which is an end iterator and dereferencing in in next iteration that invokes undefined behaviour. You should be instead, incrementing it to iterate from the last of the container.
Last but not least, you should only go to half of the container to reverse it. Otherwise, you will be swapping twice and will get the same vector you passed.
Following is the corrected code: See a demo
#include <iostream>
#include <iterator>
#include <algorithm> // std::copy_n, std::swap
#include <vector>
void reverseString(std::vector<char>& s)
{
auto i = s.begin();
auto j = s.rbegin();
const auto half = s.size() / 2u;
while (i != s.begin() + half || j != s.rbegin() + half)
{
// your saping code or simply `std::swap` the elements
std::swap(*i, *j);
++i;
++j; // increment both iterators
}
}
int main()
{
std::vector<char> vec{ 'a', 'b', 'c' };
reverseString(vec);
// print the reversed vector
std::copy_n(vec.cbegin(), vec.size(), std::ostream_iterator<char>(std::cout, " "));
}
Output:
c b a
As a side note:
You can have a look at the standard algorithm
std::reverse
from
<algorithm> header,
which is more likely you want here unless you meant to practice by
reinventing.
Please don't using namespace std;
The error message already says it
('__gnu_cxx::__normal_iterator<char *, std::vector<char, std::allocator > >' and 'std::reverse_iterator<__gnu_cxx::__normal_iterator<char *, std::vector<char, std::allocator > > >')
When you remove the common, irrelevant stuff inside the angle brackets, you get
__gnu_cxx::__normal_iterator<...> and std::reverse_iterator<__gnu_cxx::__normal_iterator<...> >
This means you have two different types, for which no appropriate comparison operator is defined.
To solve this, you might compare with std::reverse_iterator::base(), e.g.
while (i != j.base()) {
// ...
}
But also note
The base iterator refers to the element that is next (...) to the element the reverse_iterator is currently pointing to.
This means, you must also check one beyond and the loop condition becomes
while (i != j.base() && i != j.base() - 1) {
// ...
}
Unrelated, but instead of doing the swap yourself manually, you might use std::iter_swap
while (i != j.base() && i != j.base() - 1) {
std::iter_swap(i, j);
++i;
++j;
}
It seems you know that you have to dereference iterators as in *i = *j;.
You also need to do that in while(i != j), because you can compare chars, but not iterators. Especially not iterators of different kinds, as dgrandm mentions in his comment.
I.e.
while(*i != *j)
This gets the type problem fixed.
It is however likely that you are actually trying to not compare the value of the elements but instead their position in the vector. I.e. in order to reverse the whole vector.
Comparing positions is not possible with iterators
(to be precise: comparing for lesser/greater, see below on the appreciated input by Alan Birtles). Because iterators intentionally hide the differences between different containers. Vectors do have a comparable position, but other containers do not have one. Think of linked lists for example.
You would not be able to compare positions in a linked list, at least not by comparing iterators. Hence the container-abstracting iterators also hide the comparability. This in turn means that you cannot compare positions via iterators, even if the container you are currently using would allow it.
To clarify: Comparing iterators is possible (like "is this the same element?"), as Alan Birtles mentions, but only for equality. The comparision you need however is for lesser/greater (like "did these two iterators pass each other?"). This becomes apparent if you think of containes with an even number of elements. In that case with i++;j--; the two iterators never point to the same element and your loop would fail. You could equality-check before AND after changing the second iterator, if that were possible for the two DIFFERENT iterators you are using.
I have a function which needs to divide a vector up into n sub-vectors.
For example, it might look something like this:
void foo(std::vector<uint8_t> vec, int numSubVectors){
size_t size = vec.size() / numSubVectors;
auto iter = vec.begin();
for (int i = 0; i < numSubVectors; ++i) {
auto sub_vec = std::vector<uint8_t>(iter, iter + size);
// do something with sub_vec
// ...
iter += size;
}
}
I need this to work when called with foo({}, 1), where sub_vec gets assigned an empty vector on the first (and only) iteration of the loop.
I'm concerned about the std::vector<uint8_t>(iter, iter + size). Does the c++ standard allow a vector to be constructed using its range constructor when first == last?
According to cplusplus.com, "The range used is [first,last), which includes all the elements between first and last, including the element pointed by first but not the element pointed by last", but that statement doesn't make any sense when first == last?
I tried running it on an online IDE and it seems to work (https://ideone.com/V9hylA), so it's clearly not prohibited, but is it undefined behaviour?
From iterator.requirements.general of the standard:
An iterator and a sentinel denoting a range are comparable. A range [i, s) is empty if i == s; otherwise [...]
So when first == last, the standard explicitly defines this as an empty range.
The iterator pair [first, last) where first == last is how we define an empty range. It's syntactically and logically valid. Constructing a std::vector from that iterator pair will do the correct thing and create an empty container.
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.
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.
I have a vector of IntRect: vector.
How can I iterate from both ends of the list and stop the iterator when the iterator intersects?
vector<IntRect>::iterator itr = myVector.begin();
vector<IntRect>::reverse_iterator revItr.rbegin();
for (; /*check itr and revItr does not intersect, and itr and revItr do not end */ ; ++itr, ++revItr) {
IntRect r1 = *itr;
IntRect r2 = *revItr;
// do something with r1 and r2
}
Thank you.
if(!myVector.empty()) {
for(vector<IntRect>::iterator forwards = myVector.begin(),
backwards = myVector.end()-1;
forwards < backwards;
++forwards, --backwards) {
// do stuff
}
}
I think you need to check empty() with that implementation - suspect that end()-1 isn't defined if the vector is empty. I haven't used it before, but Dinkumware STL at least has operator < defined for vector iterators and it appears to do something sensible.
Also note that you need to check <, not just equality - that takes care of the (common) case where your vector has an even number of entries and the two iterators would step past one another.
You can use base function on the reverse iterator and compare the result with your forward iterator.
Remember that if you're moving both iterators, they will never be equal if the sequence has odd number of elements. You have to check the equality twice in each iteration.
None of the answers that I've seen account for the two iterators "passing in the night."
vector<IntRect>::iterator forward = myVector.begin(), backward = myVector.end();
while (forward != backward)
{
++forward;
// at this point they could be equal
if (forward == backward)
break;
--backward;
}
Your iterators point to the same thing if &(*itr) == &(*revItr)
Assuming nobody has done something stupid and overloaded operator& on IntRect.
I would replace the second (reverse) iterator with a regular one and have that initialised to --myVector.end(). Instead of incrementing it, decrement it and compare the iterator values.