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.
Related
In other words, what I mean to say is : Is itr+=2 a valid argument in c++ ?, where (itr is an iterator to first element of the set). If so, then the following piece of code should work:
In this piece if code, the code written in /comment section/ functions well, while the code not in comment section do not. Help me out to iterate alternate elements.
#include <bits/stdc++.h>
using namespace std;
int main()
{
set<int> s;
s.insert(5);
s.insert(7);
s.insert(8);
auto it=s.begin();
cout<<*it<<'\n';
it+=2;
cout<<*it<<'\n';
/*for(auto it=s.begin();it!=s.end();it++)
cout<<*it<<" ";*/
return 0;
}
Is itr+=2 a valid argument in c++?
It depends on the container type. For example, it would be perfectly valid for std::vector or std::array, but not for std::set. Each container, due to its nature, provides different types of iterators. std::set only provides BidirectionalIterator, which do not support jumping over arbitrary number of elements, only incrementation and decrementation.
However, you can use std::advance() from <iterator> library (or just increment the iterator twice). Beware that you must never increment end() iterator, so you need to take it into account in loop condition.
for(auto it=s.begin(); it != s.end() && it != std::prev(s.end()); std::advance(it, 2))
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
}
}
I have a std::list<int> and a std::vector<int>. I want to remove even elements from them, and duplicate odd element in them.
I've two different functions for both of them:
Vector:
std::vector<int> vec_remove_even_duplicate_odd(std::vector<int> target) {
std::vector<int>::iterator begin = target.begin();
while (begin != target.end()) {
if (*begin % 2 == 0) {
begin = target.erase(begin);
} else {
begin = target.insert(begin, *begin);
begin += 2;
}
}
return target;
}
This works fine. But the same function for std::list<int> shows error at the line begin += 2:
error: no match for ‘operator+=’ (operand types are ‘std::list<int>::iterator {aka std::_List_iterator<int>}’ and ‘int’)
If I change it to:
begin = begin + 2
it shows the following note:
note: mismatched types ‘const std::reverse_iterator<_Iterator>’ and ‘int’
But, if I change that line to:
++begin;
++begin;
It works fine for list too. So what is it with this behaviour, that I might have missed while reading about containers.
Why is the += operator not defined for std::list<T>::iterator? And why that message for simple + operator? I haven't even created a reverse_iterator?
I'm aware that a vector is a contiguous structure, while a list is not. But how will that matter, given that post-increment is applicable?
Is this issue specific to list only, or some other container also have this issue?
Since std::list is actually a linked list, its iterators provide only the functionality that is trivial to implement in such a data structure; in particular, std::list iterators are so-called bidirectional iterators, not random access iterators, thus they do not provide neither operator+= nor operator+, hence the messages you get.
If in a generic algorithm you need to go forward of n elements, regardless of the computational cost of the operation, you can use std::advance, which will use operator+= for random iterators and repeated application of ++ or -- in the other cases.
By the way, your loop for std::vector doesn't look fine - insertion and removal in a std::vector can invalidate iterators (including those you are using to iterate over your vector); you should change the approach of your algorithm (maybe the simplest thing is just to copy the elements in a separate vector).
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.