// display vector elements using const_iterator
for ( constIterator = integers.begin();
constIterator != integers.end(); ++constIterator )
cout << *constIterator << ' ';
Can we use constIterator < integers.end()?
Thank you
operator< is only defined for random access iterators. These are provided, for example, by std::vector and std::string, containers that, in essence, store their data in contiguous storage, where iterators are usually little more than wrapped pointers. Iterators provided by, e.g., std::list are only bidirectional iterators, which only provide comparison for equality.
Traditionally, it's seen as defensive programming to use < instead of !=. In case of errors (for example, someone changes ++i to i+=2) the loop will terminate even though the exact end value is never reached. However, another view at this is that it might mask an error, while the loop running endlessly or causing a crash would make the error apparent.
Yes, and you can also use operator < for deque::(const_)iterator, but it won't work for iterators for any other containers.
The working of operator < is guaranteed because vector and deque provide a Random Access Iterator.
Related
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.
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 wanted to know if we could overload the operator < for non-random iterators like those of std::list, std::map, etc. Say for example if I overload it for std::list then :
bool operator < (std::list<T>::iterator &i1, std::list<T>::iterator &i2)
{
return (&*i1 < &*i2);
}
My main purpose is to do an iteration like this :
for (auto i = l.begin(); i < l.end(); ++i) // possible for std::vector, std::deque, etc
// I want to do this instead of i != l.end()
But the compiler says :
[Error] declaration of operator< as non-function
Anyone has any solutions ?
It just doesn't make sense to do a less than comparision on iterators that do not provide it. Using your example with a std::list the nodes of the list could be anywhere in memory. Trying to compare the addresses of the nodes is pointless as the first node could have a higher address than all of the other nodes. If that is the case then you would never loop through the list. The only way this works is to have a sentinel node(end) and every iteration check to see if you are not equal to it. By doing this you know you have not reached the end and you can continue. Once you compare equal to the end then you know you have reached end of the list.
You are working in the wrong direction. Non-random-access iterator does not support operation < for a reason. Basically, it is not possible to implement operation < in a rational way for non-random-access iterators. In your case, you should write:
for (auto i = l.begin(); i != l.end(); ++i)
instead. Or, if C++11 is supported, consider the possibility of using a range-based for loop.
As to your code:
bool operator < (std::list<T>::iterator &i1, std::list<T>::iterator &i2)
{
return (&*i1 < &*i2);
}
It is not OK in two ways.
std::list<T>::iterator is a dependent name. You need to qualify it with typename.
Template argument for T cannot be deduced from the iterator type. It must be specified explicitly.
So, how to solve the second issue? Well, I don't think it is possible. The actual type of the iterator is not specified in the standard, and is considered an implementation detail.
I'd suggest using std::remove_if (and then erase of course), this is much clearer what you are doing and should also be efficient. If you can use C++11 then the predicate can be a lambda and it's really compact.
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.
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.