Suppose I have three vectors A, B and C of same type, and I am given an iterator for the vector container, how do I find which container does it belong to?
Thanks
You can't. Your design is broken, rethink it.
The entire point of iterators is to abstract away from containers (and other collections of data), you cannot just reverse the abstraction.
Strictly answering your question, you can check like this:
vector<T>::iterator it = ...;
if (it >= A.begin() && it < A.end()) // it is in vector A
if (it >= B.begin() && it < B.end()) // or in vector B
if (it >= C.begin() && it < C.end()) // and here in vector C
This only works because vector uses a random access iterator, and won't hold for plenty of other containers like map and list. As #Baum mit Augen says, you should redesign to avoid this. Perhaps you should pass a reference to the vector you want along with the iterator?
Related
Could anyone help me understand whether there's a big difference in != and < when it comes to talk about vector iterators within a for loop?
I mean, no matter whether you use != and <, the result should be the same?
for (vector<int>::iterator i = vec.begin(); i != vec.end(); i++)
// DO STUFF
for (vector<int>::iterator i = vec.begin(); i < vec.end(); i++)
// DO STUFF
I am aware that the most common way is to use !=, but would < be a big issue if used?
operator< is only supported for random access iterators. std::vector::iterator is a random access iterator, so both i != vec.end() and i < vec.end() are supported and valid and make no difference in your example.
If you had a container that does not support random access iterators (e.g. std::list), i < list.end() would not compile.
The general recommendation is to use postfix increment only when it is necessary though because it may create an unnecessary copy when the iterator is non-trivial, so ++i is cleaner and may be faster.
Also, if the loop calls a function whose definition is not available in this translation unit vec.end() is going to be reloaded from memory on each loop iteration, which might cause an unnecessary cache miss. You can avoid that reload by saving the value into a local variable, so that the compiler is certain that the local variable is inaccessible to any other function:
for(vector<int>::iterator i = vec.begin(), j = vec.end(); i < j; ++i)
// ...
Even better, you may like to use range-for loops that avoid these performance pitfalls for you:
for(auto const& elem : vec)
// ...
The entire philosophy behind the STL part of the Standard Library (the containers, iterators and algorithms) is to minimize the programatic distinctions between the containers. They exhibit different properties but how you program them is designed to be as similar as possible.
This makes them easier to learn and easier to use generically. That means you can write one generic function (or algorithm) and have it apply to any other container (or as many as possible).
With that in mind it is beneficial to use syntax that is common to all containers and iterators where possible.
Only some containers' iterators allow < comparisons but all containers' iterators accept !=. For that reason I would recommend always using != as a matter of consistency and to facilitate your code being easily ported to a different container.
It does make a difference, although not for std::vector. All iterators are equality comparable, so != will always work. Only random access iterators are less than comparable, as is std::vector, so in your case it wouldn't be a big issue.
I would like to know if it is possible to obtain an iterator to an object inside a container (e.g. std::vector<...>) by only having access to the object inside the container, e.g. through a reference (which implies we have access to a pointer to it using the & operator). For example, normally we declare an iterator as
std::vector<int>::iterator = vec.begin();
or
std::vector<int>::iterator = next(vec.begin(), idx);
but in the first example we are most probably about to iterate through the container, in order, while in the second example we know the index of the object we require. I would like to know if we can obtain the iterator to an object without knowing at which index it resides in the container, but if we do have a reference or a pointer to it, as explained above.
It might appear that this question has already been asked here, but it seems more like the OP wanted others to fix his code, rather than answering the general question, so the answers are not so satisfactory in my opinion. Also, the answer here seems to say that we can initialize an iterator with a constructor, as shown below
std::vector<int>::iterator it(...);
but I have not been able to find any evidence of a constructor for the std::iterator class in the official documentation (and neither have I been able to find any documentation on std::vector<...>::iterator) so I am wary to use the constructor shown above, even if it compiles.
NOTE
I use std::vector as an example above, but ideally I would like this to work for any container, e.g. std::list or std::deque
Specifically for std::vector (and other contiguous containers like std::string), given a pointer to an object in the vector p, we can simply do:
auto iter = v.begin() + std::distance(v.data(), p);
This is guaranteed by the contiguity contract. Note that random access is insufficient here, the above will not work for std::deque.
For any other container, there's no easy way of doing this. You'd have to just use find_if:
auto iter = std::find_if(c.begin(), c.end(), [p](auto const& o) { return &o == p; });
For intrusive containers, the iterator will be encoded into the object itself somehow so there will be some direct mechanism for converting p to an iterator. But that will be dependent on the intrusive container itself.
You can use the find function---it returns an iterator---, supported on (almost?) all containers, to find your objects. If there are several objects which are equal under the operator==, iterate until the one with the same address has been found.
Since C++11 you can use the keyword auto to deduce the type, it makes writing the type easier.
If we know the index we can get an iterator to it bybegin() + index.
And if we don't know the index we could use the std::distance() from begin and the other iterator, which will give us an iterator to the same element.
// if we know the index
auto it1 = begin(vec) + 4;
cout << *it1;
// if we don't know the index
auto p = begin(vec) + 3;
auto it2 = begin(vec) + distance(begin(vec), p+1);
cout << *it2;
I would like to know if it is possible to obtain an iterator to an object inside a container (e.g. std::vector<...>) by only having access to the object inside the container, e.g. through a reference (which implies we have access to a pointer to it using the & operator). For example, normally we declare an iterator as
std::vector<int>::iterator = vec.begin();
or
std::vector<int>::iterator = next(vec.begin(), idx);
but in the first example we are most probably about to iterate through the container, in order, while in the second example we know the index of the object we require. I would like to know if we can obtain the iterator to an object without knowing at which index it resides in the container, but if we do have a reference or a pointer to it, as explained above.
It might appear that this question has already been asked here, but it seems more like the OP wanted others to fix his code, rather than answering the general question, so the answers are not so satisfactory in my opinion. Also, the answer here seems to say that we can initialize an iterator with a constructor, as shown below
std::vector<int>::iterator it(...);
but I have not been able to find any evidence of a constructor for the std::iterator class in the official documentation (and neither have I been able to find any documentation on std::vector<...>::iterator) so I am wary to use the constructor shown above, even if it compiles.
NOTE
I use std::vector as an example above, but ideally I would like this to work for any container, e.g. std::list or std::deque
Specifically for std::vector (and other contiguous containers like std::string), given a pointer to an object in the vector p, we can simply do:
auto iter = v.begin() + std::distance(v.data(), p);
This is guaranteed by the contiguity contract. Note that random access is insufficient here, the above will not work for std::deque.
For any other container, there's no easy way of doing this. You'd have to just use find_if:
auto iter = std::find_if(c.begin(), c.end(), [p](auto const& o) { return &o == p; });
For intrusive containers, the iterator will be encoded into the object itself somehow so there will be some direct mechanism for converting p to an iterator. But that will be dependent on the intrusive container itself.
You can use the find function---it returns an iterator---, supported on (almost?) all containers, to find your objects. If there are several objects which are equal under the operator==, iterate until the one with the same address has been found.
Since C++11 you can use the keyword auto to deduce the type, it makes writing the type easier.
If we know the index we can get an iterator to it bybegin() + index.
And if we don't know the index we could use the std::distance() from begin and the other iterator, which will give us an iterator to the same element.
// if we know the index
auto it1 = begin(vec) + 4;
cout << *it1;
// if we don't know the index
auto p = begin(vec) + 3;
auto it2 = begin(vec) + distance(begin(vec), p+1);
cout << *it2;
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);
I am a little bit frustrated of how to use vectors in C++. I use them widely though I am not exactly certain of how I use them. Below are the questions?
If I have a vector lets say: std::vector<CString> v_strMyVector, with (int)v_strMyVector.size > i can I access the i member: v_strMyVector[i] == "xxxx"; ? (it works, though why?)
Do i always need to define an iterator to acces to go to the beginning of the vector, and lop on its members ?
What is the purpose of an iterator if I have access to all members of the vector directly (see 1)?
Thanks in advance,
Sun
It works only because there's no bounds checking for operator[], for performance reason. Doing so will result in undefined behavior. If you use the safer v_strMyVector.at(i), it will throw an OutOfRange exception.
It's because the operator[] returns a reference.
Since vectors can be accessed randomly in O(1) time, looping by index or iterator makes no performance difference.
The iterator lets you write an algorithm independent of the container. This iterator pattern is used a lot in the <algorithm> library to allow writing generic code easier, e.g. instead of needing N members for each of the M containers (i.e. writing M*N functions)
std::vector<T>::find(x)
std::list<T>::find(x)
std::deque<T>::find(x)
...
std::vector<T>::count(x)
std::list<T>::count(x)
std::deque<T>::count(x)
...
we just need N templates
find(iter_begin, iter_end, x);
count(iter_begin, iter_end, x);
...
and each of the M container provide the iterators, reducing the number of function needed to just M+N.
It returns a reference.
No,, because vector has random access. However, you do for other types (e.g. list, which is a doubly-linked list)
To unify all the collections (along with other types, like arrays). That way you can use algorithms like std::copy on any type that meets the requirements.
Regarding your second point, the idiomatic C++ way is not to loop at all, but to use algorithms (if feasible).
Manual looping for output:
for (std::vector<std::string>::iterator it = vec.begin(); it != end(); ++it)
{
std::cout << *it << "\n";
}
Algorithm:
std::copy(vec.begin(), vec.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
Manual looping for calling a member function:
for (std::vector<Drawable*>::iterator it = vec.begin(); it != end(); ++it)
{
(*it)->draw();
}
Algorithm:
std::for_each(vec.begin(), vec.end(), std::mem_fun(&Drawable::draw));
Hope that helps.
Workd because the [] operator is overloaded:
reference operator[](size_type n)
See http://www.sgi.com/tech/stl/Vector.html
Traversing any collection in STL using iterator is a de facto.
I think one advantage is if you replace vector by another collection, all of your code would continue to work.
That's the idea of vectors, they provide direct access to all items, much as regular arrays. Internally, vectors are represented as dynamically allocated, contiguous memory areas. The operator [] is defined to mimic semantics of the regular array.
Having an iterator is not really required, you may as well use an index variable that goes from 0 to v_strMtVector.size()-1, as you would do with regular array:
for (int i = 0; i < v_strMtVector.size(); ++i) {
...
}
That said, using an iterator is considered to be a good style by many, because...
Using an iterator makes it easier to replace underlying container type, e.g. from std::vector<> to std::list<>. Iterators may also be used with STL algorithms, such as std::sort().
std::vector is a type of sequence that provides constant time random access. You can access a reference to any item by reference in constant time but you pay for it when inserting into and deleting from the vector as these can be very expensive operations. You do not need to use iterators when accessing the contents of the vector, but it does support them.