The advantage of iterators?

Started by
9 comments, last by ApochPiQ 17 years, 10 months ago
Been using iterators for a fair while now, but I don't know why. I mean... they're supposedly the "right" way to scan a vector, but I don't exactly get why. They also seem to be less versatile than using a loop with the [] operator or at function. Like let's have 2 examples... say you want to delete an int in a vector if it is equal to 5.
//iterator way!
for (it=vec.begin();it!=vec.end();it++)
{
   if(*it == 5)
      vec.erase(it);
}
/* Now that the iterator points to nothing, next round it'll try pointing to something that isn't in the vector and not initialized... program pukes */
int pos = 0;
for (int it=0;it!=vec.size();it++)
{
   if(vec[it]==5)
      pos = it;
}
if(pos)
   vec.erase(pos);
And since you don't know what position an iterator is pointing to, can't really do that with them. Anyways, that's not my main point... just curious what the huge advantage over them is. :) -Etyrn
Advertisement
You can A:
for ( it=vec.begin();it!=vec.end;){if ( *it == 5 )   it = vec.erase(it);else   ++it;}

or
vec.erase(remove_if( vec.begin(), vec.end(), bind2nd(equal_to<int>(),5) ) );                            

Iterators can be aquward for some situations, but when you are using any STL constuct, they take interators,
so it is the way to go.
^^ Edit: You got fist! Umm..... first! :)


That's why erase() returns an iterator to the next object in the vector. :)

it = vec.erase(it);

Iterators are faster (they are pretty much a pointer, but to use [] is a lookup:

pointer + i * sizeof(object)

They are also more flexible, as they can be used in all of the STL algorithms.

Check out my new game Smash and Dash at:

http://www.smashanddashgame.com/

Thank you. You were both very helpful. :)


-Etyrn
bool is5(int n){    return(n==5);}vec.erase(remove_if(vec.begin(),vec.end(),is5),vec.end());


Get off my lawn!

Additionally, not all of the standard library containers allow you to access their elements with the [] operator. For example:

#include <set>#include <iostream>int main(){    std::set<int> foo;    foo.insert(1);    foo.insert(5);    foo.insert(3);        // Doesn't work because elements in a std::set can't be accessed with the [] operator    // for (unsigned int i = 0; i < foo.size(); ++i)        // std::cout << foo << std::endl;    // Works    for (std::set<int>::iterator itor = foo.begin(); itor != foo.end(); ++itor)        std::cout << *itor << std::endl;    // If you won't be changing any of the elements, using a const_iterator is smarter     for (std::set<int>::const_iterator itor = foo.begin(); itor != foo.end(); ++itor)        std::cout << *itor << std::endl;}
Quote:Original post by TANSTAAFL
*** Source Snippet Removed ***


After reading your example, I didn't really like the is5... so I tried something and it seems to work (at least with VC 2005, since its a template trick, it probably doesn't work on old compilers)

template<class T, T value> bool isValue(T in_Value){	return (value == in_Value);}


so later on in your code (this is just an example...)

typedef std::vector<int> idvectors;idvectors myvect;// ... push values heremyvect.erase(std::remove_if(myvect.begin(),myvect.end(),isValue<idvectors::value_type,5>),myvect.end());


the only downside is that "5" needs to be known at compile time, and it will generate something similar to is5, but I thought it was cleaner this way, and supports any type that has == defined :)

Cheers!

Eric
a predicate is simply something that has operator (). you can make a class to do the job:

class compare_to_value{private:    int m_compare_to;public:    compare_to_value(int compare_to):m_compare_to(compare_to){}    bool operator()(int value){return(value==m_compare_to);}};int n=5;myvect.erase(std::remove_if(myvect.begin(),myvect.end()),compare_to_value(n)),myvect.end());


and you could, to make compare_to_value more reusable, make it a template class as well. I just used an int because templates make my headasplode.

Get off my lawn!

Quote:Original post by KulSeran
vec.erase(remove_if( vec.begin(), vec.end(), bind2nd(equal_to<int>(),5) ) );


The standard library recognizes that this is unnecessarily verbose and the extra weight of generated code might not all get optimized away, so it also provides the simpler version for the case of checking for equality:

vec.erase(remove( vec.begin(), vec.end(), 5 ) );

:)

Anyway:

- Iterators protect you from changes to the container type, since not all of them allow you to use operator[], and some (std::map) use operator[], but don't necessarily use it with integers in order (std::map::operator[] expects key types). With an appropriate typedef, you end up being protected from changing the code if you change the type of the container that is iterated over:

typedef std::vector<int> container;// Later, we change that to std::list<std::string>, and the loop still compiles:container x;// ...for (container::iterator it = x.begin(); it != x.end(); ++it) {  cout << *it << endl;}


- They also clean up the abstraction a little bit: Instead of "with every number in this range, grab the element at this index, and do stuff with it", you get "with an element-pointer ranging over the collection, do stuff with each element as it is pointed at". It's not as nice as the higher-level looping constructs in Python (or even C# or Java 1.5), but it's an improvement. The reason you do that is the same reason you use for() in the first place instead of reproducing that behaviour with while(), or in turn why you use while() instead of reproducing it with goto(s).

- However, for common types of looping, you should use the std::algorithm (or combination thereof) that best models what you want to accomplish.
Yeah that's nice! I never used a class as a predicate, seems pretty flexible, thanks for the tip!

This topic is closed to new replies.

Advertisement