Can I do normal computations with iterators, i.e. just increment it by adding a number?
As an example, if I want to remove the element vec[3], can I just do this:
std::vector<int> vec;
for(int i = 0; i < 5; ++i){
vec.push_back(i);
}
vec.erase(vec.begin() + 3); // removes vec[3] element
It works for me (g++), but I'm not sure if it is guaranteed to work.
It works if the iterator is a random access iterator, which vector's iterators are (see reference). The STL function std::advance can be used to advance a generic iterator, but since it doesn't return the iterator, I tend use + if available because it looks cleaner.
C++11 note
Now there is std::next and std::prev, which do return the iterator, so if you are working in template land you can use them to advance a generic iterator and still have clean code.
A subtle point is that the operator+ takes a Distance; i.e., a signed integer. If you increment the iterator by an unsigned, you may lose precision and run into a surprise. For example on a 64 bit system,
std::size_t n = (1 << 64) - 2;
std::vector<double> vec(1 << 64);
std::vector<double> slice(vec.begin() + n, vec.end());
leads to implementation-defined behavior. With g++ or clang, you can ask the compiler to warn you about such undesired conversions with the warning flag -Wsign-conversion that is not part of the canonical -Wall or -Wextra.
A work-around is to work on the pointer directly
std::vector<double> slice(vec.data() + n, vec.data() + vec.size());
It's not pretty but correct. In some occasions, you need to construct the iterator manually, for example
std::vector<double>::iterator fromHere{vec.data() + n};
vec.erase(fromHere, vec.end());
It works with random access iterators. In general you may want to look at std::advance which is more generic. Just be sure to understand performance implications of using this function template.
Number arithmetic is possible only with random access iterators such as those in std::vector and std::deque.
std::vector<int>list={1,2,3,4,5,6,7,8};
auto last_v=*(list.end()-1);
auto third_last_v=*(list.end()-3);
std::cout<<"Last & 3rd last entry for vector:"<<last_v<<","<<third_last_v<<std::endl;
will output 8 and 6, however for std::map, std::multimap, std::set, std::multiset having bidirectional iterator
std::map<int,std::string> map_={{1,"one"},{2,"two"},{3,"three"}};
auto last_mp=*(map_.end()-1);
auto third_last_mp=*(map_.end()-3);
std::cout<<"Last & 3rd last entry for map:("<<last_mp.first<<","<<last_mp.second<<") and ("<<third_last_mp.first<<","<<third_last_mp.second<<")"<<std::endl;
will result in
error: no match for ‘operator-’ (operand types are ‘std::map, int>::iterator {aka std::_Rb_tree_iterator, int> >}’ and ‘int’)
For bidirectional iterator std::next(),std::prev or std::advance() works
std::map<int,std::string> map_={{1,"one"},{2,"two"},{3,"three"}};
auto last_mp=*std::prev(map_.end(),1);
auto third_last_mp=*std::prev(map_.end(),3);
std::cout<<"Last & 3rd last entry for map:("<<last_mp.first<<","<<last_mp.second<<") and ("<<third_last_mp.first<<","<<third_last_mp.second<<")"<<std::endl;
will output (3,three) and (1,one). On a similar note std::unordered_map has a forward iterator, so here std::next() makes sense to use.
Related
Life gave me the following objects:
std::vector<T1> v1;
std::vector<T2> v2;
typename std::vector<T1>::iterator it_first;
typename std::vector<T1>::iterator it_last;
and the following constraints:
v1.size() == v2.size() > 0
v1.begin() <= it_first <= it_last <= v1.end()
Removing from v1 the range pointed by the two iterators is a trivial single line, but how do I remove the same range also from v2?
I can easily solve this for instance by building v2 iterators using a mix of std::distance/advance, but I was wondering if the STL provides some machinery for this. Something like the erase-remove idiom coupled with a transform operation, maybe? It seems beyond my STL-fu...
When you have an iterator, then you can get the index via std::distance to begin().
When you have an index, then you can get the iterator via begin() + index.
Thats bascially all you need to get the same range of incdices in the second vector.
Btw iterators are to abstract away the index. When you need the index, then work with the index. For erase that would be
size_t start = 1;
size_t end = 42;
std::erase( v1.begin() + start, v1.begin() + end );
std::erase( v2.begin() + start, v2.begin() + end );
I can easily solve this for instance by building v2 iterators using a mix of std::distance/advance, but I was wondering if the STL provides some machinery for this. Something like the erase-remove idiom coupled with a transform operation, maybe? It seems beyond my STL-fu...
I saw the edit only after writing the answer. The thing is std::distance / std::advance is the machinery to switch between iterators and indices.
Consider that iterators in general are for things that can be iterated. You do not even necesarily need a container to iterate. For example you could write an iterator type that lets you iterate integers. This iterator type could be used like this:
std::vector<std::string> x(100);
auto begin = int_iterator(0);
auto end = int_iterator(100);
for ( ; begin != end; ++begin) std::cout << x[ *begin ];
This is no C-ism. The example is perhaps not the best, but the int_iterator itself is on par with the general concept of iterators.
Usually iterators have no knowledge about the underlying container (if there is one). There are few exceptions (eg insert_iterator). And in case of erase of course you need to pass iterators that refer to element in the corresponding container.
I have a iterator from a std::list<std::string>, but when I attempt to advance it using +=, I get a compilation error.
The code is:
#include <list>
#include <iostream>
#include <string>
int main() {
std::list<std::string> x;
x.push_front("British");
x.push_back("character");
x.push_front("Coding is unco");
x.push_back("Society");
x.push_back("City Hole");
auto iter = x.begin();
iter += 3;
//std::advance(iter, 3);
x.erase(iter);
for (auto &e: x) {
std::cout << e << "\n";
}
}
If I compile this using clang++ -std=c++11 -o li li.cpp, I get:
li.cpp:13:10: error: no viable overloaded '+='
iter += 3;
~~~~ ^ ~
1 error generated.
Why can't I use += with this iterator?
The iterator for std::list is BidirectionalIterator, which doesn't support operator+= like RandomAccessIterator.
You can use operator++, which is supported by InputIterator (including BidirectionalIterator), something like
++iter;
++iter;
++iter;
But it's ugly. The best way is as you commented, to use std::advance (or std::next (since C++11)) instead, which could be used with InputIterator (including BidirectionalIterator), and also takes advantage of the features supported by RandomAccessIterator.
(emphasis mine)
Complexity
Linear.
However, if InputIt additionally meets the requirements of
RandomAccessIterator, complexity is constant.
So you can just use it without considering about the category of the iterator, std::advance will do the best choice for you. e.g.
std::advance(iter, 3);
or
iter = std::next(iter, 3);
The reason is simply that the += operator is not defined for the Bidirectional iterator you are using.
For all iterators there is at least:
Copy-assignable and destructible, i.e. X b(a); and b = a;
Can be incremented, i.e. ++a and a++
Everything else depends on the type of iterator check the table here:
As you see a random-access iterator would do the trick.
A std::list::iterator is not a Random Access Iterator. It is not possible to "jump ahead" several elements in a list, you must iterate through the list until you reach the desired element. You can use std::advance which will deduce the best way to advance the iterator based on the iterator category. In the case of a std::list::iterator it will increment the iterator in a loop.
Apologies if this was already asked. I couldn't find an answer to that.
Is it legal to use an iterator as an offset index?
For example:
for (list<T> iterator::it = v2->vals.begin(); it!=v2->vals.end();++i) {
v3.push_back(v2[it] + v1[it]);
}
where : const Vec& v2
and vals is a list in Vec class's protected.
Many thanks!
You can't directly use iterators as indexes.
However, if you do want to use the iterators "position" in its container to have the index you want, you can do so with std::distance, i.e.:
unsigned int index = std::distance(v2.begin(), it);
//if it was the fifth element, index's value is 4.
If v1 and v2 are instances of a custom data type, then yes it's possible, because you can then create an overloaded operator[] functions which takes the correct iterator types as arguments.
However it's not as easy as it seems for you, since at least v2 seems to be a pointer, so you need to either dereference it first:
(*v2)[it]
or call the operator function explicitly:
v2->operator[](it)
For parallel traversal of multiple containers, you can use Boost.Iterator's Zip Iterator :
std::transform(
boost::make_zip_iterator(boost::make_tuple(begin(v1), begin(v2))),
boost::make_zip_iterator(boost::make_tuple( end(v1), end(v2))),
std::back_inserter(v3),
[](boost::tuple<int const &, int const&> const &p) {
return p.get<0>() + p.get<1>();
}
);
I'm admittedly not really convinced by the syntax though. IMHO this library would benefit from some C++11/14 peppering.
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 it possible to compare two iterators? A comparision using std::min
void change ( typename TList <Item *>::Type ::iterator it_begin, typename TList <Item*>::Type ::iterator it_end )
{
....
this->items.resize ( index );
std::sort ( it_begin, std::min (it_end, it_begin += index - 1); //Compare two iterators, exception
....
}
throws the following exception:
Assertion failed: Vector iterators incompatible...
Is there any other way of the comparision?
Yes. But I doubt if you can do that with std::min.
You can use std::distance function to calculate the distance between two iterators. And then you can use the distance to determine which iterator is the smaller one. Once you know the smaller iterator, you can pass that to std::sort function.
Here is small illustration how to calculate distance:
#include <iostream>
#include <iterator>
#include <vector>
int main() {
std::vector<int> v(100); //vector of size 100
std::cout <<(std::distance(v.begin(), v.begin() + 10))<< std::endl;
std::cout <<(std::distance(v.begin() +25, v.begin() +10))<< std::endl;
}
Output:
10
-15
Hope that gives you enough idea how to proceed to do what you want to.
In the book C++ Primer 5th Ed. on p.111 section 3.4.2 Iterator Arithmetic says,
we can use == and != to compare to valid iterators into any of the library containers.
The section also tells us that iterators for string and vector support relational operators (aka iterator arithmetic) which include >, >=, <, <=.
To answer the question, std::distance() can be used to measure the distance to the begin() iterator, and these distances can then be compared. However, as pointed out by Ben, there are other problems with your code. See http://www.cplusplus.com/reference/std/iterator/distance/
After calling resize, all your existing iterators are invalid.
Furthermore, that line invokes undefined behavior, since you're both changing it_begin and reading from it, in an undetermined order.