Can the iterator be shared between vectors in C++? - c++

In c++, if I have two arrays a[10] and b[10], I can introduce an index i can be used for both arrays points to the (i+1)-th element, a[i] and b[i]. Is the iterator can be shared as well or I need to do something like:
vector<int> a;
vector<int> b; //assume both are initiated and same
vector<int>::iterator i; //assume program know i=10 and *i=20 in vector a
vector<int>::iterator j = b.begin();
for(;j != b.end();j++){
if(*j == *i){
break;
}
}
and then I get the iterator j which points to the same position but in vector b? Can I simply know the position of i in a and then j=b.begin()+pos(i)

Iterators in the Standard C++ Library are modeled after pointers, not after indexes. In the same way that pointers cannot be shared among multiple arrays, iterators cannot be shared among vectors or other collections.
If you need to iterate multiple vectors in parallel, you can use indexes rather than iterators. If you need to find the position of an iterator, you can use std::distance like this:
std::distance(b.begin(), my_iter)
If you need to move an iterator i in vector a to the same position as iterator j in vector b, you can do this:
auto i = a.begin();
// j is an iterator in vector "b"
advance(i, distance(b.begin(), j));

You could use the following:
#include <iterator>
j = b.begin() + distance(i, a.begin());

No, that's not possible. Iterators cannot be shared between two containers. If you want to have a common index for iterating over the two vectors, use operator[] instead.

Can iterator be shared between vectors?
No, each iterator variable can refer to an element of at most one container, and it is not legal to compare iterators referring to elements in different containers.
But... in the code if(*j == *i) you are not comparing the iterators, but rather the values that the iterators refer to. This is perfectly legal, regardless of the containers that the iterators i and j refer to (as long as they are dereference-able, i.e. they refer to a valid element).
and then I get the iterator j which points to the same position but in vector b?
This does not make sense in the context of the code above. If what you mean to do is selecting an element in one container and the iterator in the second container that refers to the same position (index), then you can do that:
j = std::next(b.begin(),std::distance(a.begin(),i));
Then again, there are algorithms that could be used for the same purpose:
std::find() // find an element equal to the last argument
std::find_if() // find an element that matches a functor

No, you can not compare iterators for different collections, not even of the same type and with equal data. Just because the two vectors appear to be the same, they are two different objects, and the memory occupied by the underlying data is therefore different.

A cleaner way of doing this would be by using std::find() as follows:
j = std::find(b.begin(), b.end(), *i);

Related

What is the advantage of using (it != vector.end()) instead of (it < vector.end()) in for loops? [duplicate]

I'm used to writing loops like this:
for (std::size_t index = 0; index < foo.size(); index++)
{
// Do stuff with foo[index].
}
But when I see iterator loops in others' code, they look like this:
for (Foo::Iterator iterator = foo.begin(); iterator != foo.end(); iterator++)
{
// Do stuff with *Iterator.
}
I find the iterator != foo.end() to be offputting. It can also be dangerous if iterator is incremented by more than one.
It seems more "correct" to use iterator < foo.end(), but I never see that in real code. Why not?
All iterators are equality comparable. Only random access iterators are relationally comparable. Input iterators, forward iterators, and bidirectional iterators are not relationally comparable.
Thus, the comparison using != is more generic and flexible than the comparison using <.
There are different categories of iterators because not all ranges of elements have the same access properties. For example,
if you have an iterators into an array (a contiguous sequence of elements), it's trivial to relationally compare them; you just have to compare the indices of the pointed to elements (or the pointers to them, since the iterators likely just contain pointers to the elements);
if you have iterators into a linked list and you want to test whether one iterator is "less than" another iterator, you have to walk the nodes of the linked list from the one iterator until either you reach the other iterator or you reach the end of the list.
The rule is that all operations on an iterator should have constant time complexity (or, at a minimum, sublinear time complexity). You can always perform an equality comparison in constant time since you just have to compare whether the iterators point to the same object. So, all iterators are equality comparable.
Further, you aren't allowed to increment an iterator past the end of the range into which it points. So, if you end up in a scenario where it != foo.end() does not do the same thing as it < foo.end(), you already have undefined behavior because you've iterated past the end of the range.
The same is true for pointers into an array: you aren't allowed to increment a pointer beyond one-past-the-end of the array; a program that does so exhibits undefined behavior. (The same is obviously not true for indices, since indices are just integers.)
Some Standard Library implementations (like the Visual C++ Standard Library implementation) have helpful debug code that will raise an assertion when you do something illegal with an iterator like this.
Short answer: Because Iterator is not a number, it's an object.
Longer answer: There are more collections than linear arrays. Trees and hashes, for example, don't really lend themselves to "this index is before this other index". For a tree, two indices that live on separate branches, for example. Or, any two indices in a hash -- they have no order at all, so any order you impose on them is arbitrary.
You don't have to worry about "missing" End(). It is also not a number, it is an object that represents the end of the collection. It doesn't make sense to have an iterator that goes past it, and indeed it cannot.

Calling std::sort

sort in the C++ standard library is called as:
sort (first element, last element);
So if I have an array:
int a[n];
I should call sort as:
sort(&a[0], &a[n-1]);
since a[0] is the first element and a[n-1] the last. When I do so, however, it doesn't sort the last element. To get a fully sorted array, I must use:
sort(&a[0], &a[n]);
Why is this?
Because ranges in stl are always defined as half-open ranges from the fist element iterator to to the "one-past-the-end"-iterator. With C++11 you can use:
int a[n];
sort(std::begin(a),std::end(a));
Format for sort in STL in c++ is,
sort (first element, last element);
No, it's not. You are supposed to provide an iterator for the first element, and a one-past-the-end iterator, as you've discovered.
The Standard Library in general uses semi-open intervals to describe ranges through iterators. Otherwise it would be impossible for empty ranges to be expressed:
// An empty container!
std::vector<int> v;
// Pretend that `v.end()` returns an iterator for the actual last element,
// with the same caveat as `v.begin()` that the case where no elements
// exist gives you some kind of "sentinel" iterator that does not represent
// any element at all and cannot be dereferenced
std::vector<int>::iterator a = v.begin(), b = v.end();
// Oh no, this would think that there's one element!
std::sort(a, b);

How does sorting in c++ work?

I'm a Java developer. I'm currently learning C++. I've been looking at code samples for sorting. In Java, one normally gives a sorting method the container it needs to sort e.g
sort(Object[] someArray)
I've noticed in C++ that you pass two args, the start and end of the container. My question is that how is the actual container accessed then?
Here's sample code taken from Wikipedia illustrating the the sort method
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> vec;
vec.push_back(10); vec.push_back(5); vec.push_back(100);
std::sort(vec.begin(), vec.end());
for (int i = 0; i < vec.size(); ++i)
std::cout << vec[i] << ' ';
}
vec.begin() and vec.end() are returning iterators iterators. The iterators are kind of pointers on the elements, you can read them and modify them using iterators. That is what sort is doing using the iterators.
If it is an iterator, you can directly modify the object the iterator is referring to:
*it = X;
The sort function does not have to know about the containers, which is the power of the iterators. By manipulating the pointers, it can sort the complete container without even knowing exactly what container it is.
You should learn about iterators (http://www.cprogramming.com/tutorial/stl/iterators.html)
vec.begin() and vec.end() do not return the first and last elements of the vector. They actually return what is known as an iterator. An iterator behaves very much like a pointer to the elements. If you have an iterator i that you initialised with vec.begin(), you can get a pointer to the second element in the vector just by doing i++ - the same as you would if you had a point to the first element in an array. Likewise you can do i-- to go backwards. For some iterators (known as random access iterators), you can even do i + 5 to get an iterator to the 5th element after i.
This is how the algorithm accesses the container. It knows that all of the elements that it should be sorting are between begin() and end(). It navigates around the elements by doing simple iterator operations. It can then modify the elements by doing *i, which gives the algorithm a reference to the element that i is pointing at. For example, if i is set to vec.begin(), and you do *i = 5;, you will change the value of the first element of vec.
This approach allows you to pass only part of a vector to be sorted. Let's say you only wanted to sort the first 5 elements of your vector. You could do:
std::sort(vec.begin(), vec.begin() + 5);
This is very powerful. Since iterators behave very much like pointers, you can actually pass plain old pointers too. Let's say you have an array int array[] = {4, 3, 2, 5, 1};, you could easily call std::sort(array, array + 5) (because the name of an array will decay to a pointer to its first element).
The container doesn't have to be accessed. That's the whole point of the design behind the Standard Template Library (which became part of the C++ standard library): The algorithms don't know anything about containers, just iterators.
This means they can work with anything that provides a pair of iterators. Of course all STL containers provide begin() and end() methods, but you can also use a regular old C array, or an MFC or glib container, or anything else, just by writing your own iterators for it. (And for C arrays, it's as simple as a and a+a_len for the begin and end iterators.)
As for how it works under the covers: Iterators follow an implicit protocol: you can do things like ++it to advance an iterator to the next element, or *it to get the value of the current element, or *it = 3 to set the value of the current element. (It's a bit more complicated than this, because there are a few different protocols—iterators can be random-access or forward-only, const or writable, etc. But that's the basic idea.) So, if `sort is coded to restrict itself to the iterator protocol (and, of course, it is), it works with anything that conforms to that protocol.
To learn more, there are many tutorials on the internet (and in the bookstore); there's only so much an SO answer can explain.
begin() and end() return iterators. See e.g. http://www.cprogramming.com/tutorial/stl/iterators.html
Iterators act like references into part of a container. That is, *iter = z; actually changes one of the elements in the container.
std::sort actually uses a swap function on references to the contained objects, so that any iterators you have already initialized remain in the same order but the values those iterators refer to are changed.
Note that std::list also has member functions called sort. It works the other way around: any iterators you have already initialized keep the same values, but the order of those iterators changes.

Why is "!=" used with iterators instead of "<"?

I'm used to writing loops like this:
for (std::size_t index = 0; index < foo.size(); index++)
{
// Do stuff with foo[index].
}
But when I see iterator loops in others' code, they look like this:
for (Foo::Iterator iterator = foo.begin(); iterator != foo.end(); iterator++)
{
// Do stuff with *Iterator.
}
I find the iterator != foo.end() to be offputting. It can also be dangerous if iterator is incremented by more than one.
It seems more "correct" to use iterator < foo.end(), but I never see that in real code. Why not?
All iterators are equality comparable. Only random access iterators are relationally comparable. Input iterators, forward iterators, and bidirectional iterators are not relationally comparable.
Thus, the comparison using != is more generic and flexible than the comparison using <.
There are different categories of iterators because not all ranges of elements have the same access properties. For example,
if you have an iterators into an array (a contiguous sequence of elements), it's trivial to relationally compare them; you just have to compare the indices of the pointed to elements (or the pointers to them, since the iterators likely just contain pointers to the elements);
if you have iterators into a linked list and you want to test whether one iterator is "less than" another iterator, you have to walk the nodes of the linked list from the one iterator until either you reach the other iterator or you reach the end of the list.
The rule is that all operations on an iterator should have constant time complexity (or, at a minimum, sublinear time complexity). You can always perform an equality comparison in constant time since you just have to compare whether the iterators point to the same object. So, all iterators are equality comparable.
Further, you aren't allowed to increment an iterator past the end of the range into which it points. So, if you end up in a scenario where it != foo.end() does not do the same thing as it < foo.end(), you already have undefined behavior because you've iterated past the end of the range.
The same is true for pointers into an array: you aren't allowed to increment a pointer beyond one-past-the-end of the array; a program that does so exhibits undefined behavior. (The same is obviously not true for indices, since indices are just integers.)
Some Standard Library implementations (like the Visual C++ Standard Library implementation) have helpful debug code that will raise an assertion when you do something illegal with an iterator like this.
Short answer: Because Iterator is not a number, it's an object.
Longer answer: There are more collections than linear arrays. Trees and hashes, for example, don't really lend themselves to "this index is before this other index". For a tree, two indices that live on separate branches, for example. Or, any two indices in a hash -- they have no order at all, so any order you impose on them is arbitrary.
You don't have to worry about "missing" End(). It is also not a number, it is an object that represents the end of the collection. It doesn't make sense to have an iterator that goes past it, and indeed it cannot.

The behavior of overlapped vector::insert

Where does the C++ standard declare that the pair of iterators passed to std::vector::insert must not overlap the original sequence?
Edit: To elaborate, I'm pretty sure that the standard does not require the standard library to handle situations like this:
std::vector<int> v(10);
std::vector<int>::iterator first = v.begin() + 5;
std::vector<int>::iterator last = v.begin() + 8;
v.insert(v.begin() + 2, first, last);
However, I was unable to find anything in the standard, that would prohibit the ranges [first, last) and [v.begin(), v.end()) to overlap.
23.1.1/4 Sequence requirements has:
expression: a.insert(p,i,j)
return type: void
precondition: i,j are not iterators into a. inserts copies of elements in[i,j) before p.
So i and j cannot be iterators into your vector.
It makes sense, as during the insert operation, the vector may need to resize itself, and so the existing elements may first be copied to a new memory location (there by invalidating the current iterators).
Consider the behavior if it was allowed. Every insert into the vector would both increase the distance between the start and end iterator by one and move the start iterator up one. Therefore the start iterator would never reach the end iterator and the algorithm would execute until an out of memory exception occurred.