11

I wonder why in C++ we do not (a majority) do not use Java style iterators? I mean I disagree with almost everything what's connected to Java but their iterators (from safety point of view but not only) are in my opinions much better than C++'s. Just to demonstrate some code:

C++:

std::string tmp = "Some text";
auto beg_ = tmp.cbegin(), 
end_ =  tmp.cend();

while (beg_ != end_)
{
cout << *beg_++;
} 

Java:

String tmp = "Some text";
//here I'm stretching little bit (they do not have iterators
// for string would you believe it)  
//but for sakeness of simplicity lets assume that they have it, 
//it would look identical for
//any other container
Iterator<String> iter = tmp.iterator();
while (iter.hasNext())
{
System.out.println(iter.next());
}

I think that if one would like to use an iterator just for iterating purposes I have to say that Java's iterator is both simpler in use, more intuitive and what's most important safer (can't go over boundary). I wonder then why in C++ we do prefer not to use them?

21 accepted

Well sure, you can write excessively clumsy code if you really want to.

Or you can get to the point, and write only what you need to:

std::string tmp = "Some text";
std::for_each(tmp.begin(), tmp.end(), [](char c){ cout << c;});

Edit in response to comment: This first example uses a lambda expression, a feature from C++0x. It is essentially an unnamed function declared inline where it's needed. The [] specify the function's closure, the set of variables declared outside the function that should be visible to it. In this case, the set is empty. If I'd written [tmp], it would be able to access the string tmp even though it's not a parameter to the lambda function. The next part is basically just a function definition without the name. It takes a string iterator as its argument, and sends its value to cout.

Or

std::string tmp = "Some text";
std::copy(tmp.begin(), tmp.end(), std::ostream_iterator<char>(cout));

Or you can iterate over only the first 3 characters, for example

std::string tmp = "Some text";
std::copy(tmp.begin(), tmp.begin()+3, std::ostream_iterator<char>(cout));

Or find the first 'm', and print out from there:

std::string tmp = "Some text";
std::string::iterator m_it = tmp.find('m');
std::copy(m_it, tmp.end(), std::ostream_iterator<char>(cout));

C++ iterators have quite a bit more flexibility. One of the nice things about C++ iterators is that they can be used with algorithms, abstracting away the loop entirely. I don't need to write a for loop (or even use a foreach). If I want to sort a sequence of data, I can call sort with a pair of iterators. If I want to copy a sequence of data, I can call copy with a pair of iterators, and so on.

Another important reason why C++ uses this type of iterators is that they look like pointers. In other words, if you have a plain C++ array, a pointer into the array can be used as an iterator. That's a pretty powerful feature, ensuring interoperability with a lot of legacy C code. But it requires C++ iterators to use this kind of syntax.

And there's the other advantage:

C++ iterators are efficient. Using them carries literally no overhead. This code is just as efficient as hand-coded loops.

The same can not be said about Java iterators.

13

Java's iterators must be class objects (because they have methods next(), hasNext(), etc.).

In C++, on the other hand, iterators grew as an abstraction/generalization of pointers--and thus support the set of built-in operators that pointers support. In fact, most implementations of std::vector (at least in release mode) use pointers as iterators.

In my opinion, they're both equally good and fit the paradigms of their respective languages quite well.

7

The main advantage of C++ style iterators is that they describe ranges rather than 'all of a collection'. This makes it much easier to iterate over part of a collection rather than the whole thing.

6

First of all, if you're just considering syntactical sugar and overlook all the generic, flexible range-based aspects and algorithms that C++ iterators provide over Java iterators, then you should first learn how to write a concise for loop with proper scoping.

string tmp = "Some text";
for (auto it = tmp.begin(), end = tmp.end(); it != end; ++it)
    cout << *it;

Or:

string tmp = "Some text";
BOOST_FOREACH(char ch, tmp)
    cout << ch;

You're also using auto which is not standard yet, but C++0x will make you very happy:

string tmp = "Some text";
for (char ch: tmp)
    cout << ch;

When it comes to avoiding beginner type mistakes like iterating past a sequence when the code is attempting to iterate through each element in the sequence and not doing anything fancy, you have plenty of syntactical sugar (see BOOST_FOR_EACH as an example) and even more coming with C++0x.

Now when we consider the public interfaces and design of the iterators in Java and C++, I think a further study of C++ will convince you that C++ has a superior model. This will become even more evident when you start implementing your own standard-compliant data structures. Just by providing iterators, you'll already have sorting algorithms, search algorithms, reverse algorithms, etc. already implemented for you without having to add anything more to the data structure you just wrote.

When it comes to the existing implementation and not the design, then Java iterators are safer in that respect (ex: detecting when iterators are invalidated). But there's nothing about the iterator design in C++ that would prevent you from writing safe alternatives. The reason these safety mechanisms generally don't exist in the C++ standard library is because the STL collections were primarily developed for efficiency, generality, and flexibility - not safety which inevitably has runtime costs. Stephanov, in particular, was primarily motivated in trying to find ways to provide cost-free abstraction which is one of the great strengths C++ has over many other popular languages today.

I'd also like throw in my two cents about safety in this respect. With the kind of code you posted above, if you're iterating out of boundaries, that's a silly programmer mistake. Throwing an exception in this case is, at best, a production-type workaround to the problem but it doesn't correct your mistake. Java iterators do have safety mechanisms that work to do more than work around bugs (ex: multiple threads), but in the way you are talking about using iterators for simple foreach-type loops, the safety really doesn't bear much significance.

5

While I agree with jalf's answer, if you want Java style iterators they're trivial to write:

template <typename T>
class Iterator
{
public:
  typedef typename T::iterator IterType;
  Iterator(T& source) :
    begin(source.begin()),
    end(source.end()),
    current(begin)
  {
  }

  bool hasNext() const
  {
    return current != end;
  }

  IterType next()
  {
    return current++;
  }

private:
  IterType begin;
  IterType end;
  IterType current;
};

And to use it:

int main()
{
  vector<string> temp;
  temp.push_back("Some text 1");
  temp.push_back("Some text 2");

  Iterator<vector<string> > iter(temp);
  while(iter.hasNext())
  {
    cout << *iter.next() << endl;
  }


  string temp2 = "another test string";
  Iterator<string> iter2(temp2);
  while(iter2.hasNext())
  {
    cout << *iter2.next() << endl;
  }
}

See it in action on a vector and a string.

Edit: As Konrad pointed out, you can return a reference instead of an iterator to be even more Java-like (but less trivial to write), and as Drew pointed out, Java throws if you call next() at the end:

  typename iterator_traits<typename T::iterator>::reference next()
  {
    if (current == end)
    {
      throw range_error("Used next() after hasNext() returned false!");
    }
    return *current++;
  }

This allows you to avoid having to dereference an iterator in the loop:

  while(iter2.hasNext())
  {
    cout << iter2.next() << endl;
  }

[Code]

4

When you say "we prefer not to use them" - that's somewhat misleading.

Part of it is the STL is built around a specific form of iterator. It predates Java, which tried to simplify the usage of iterators after the STL was already released.

That being said, many libraries DO support this form of iterator in C++ - Qt comes to mind. Qt 4 specifically added the ability to use Java's style of iterators because they felt that they were simpler.

0

FYI, nobody is using the code you showed in Java any more (except where needed, e.g. to invoke remove) because it?s unsafe and complicated ? contrary to your claim that it?s ?intuitive and ? safer?.

Instead, Java allows (and has allowed for some time now) a kind of ?for each? loop:

List<Character> tmp = Arrays.asList('h', 'e', 'l', 'l', 'o');

for (char c : tmp)
    System.out.println(c);

It should be obvious why this is simpler than your professed ?intuitive? code. As for why it?s safer, consider this code:

Iterator<Character> iter = tmp.iterator();

while(iter.hasNext())
    iter.next();

iter.next(); // throws NoSuchElementException

Granted, that?s not a very realistic error but it is possible, and I am not aware that C++ iterators should be any more error-prone if user correctly and idiomatically.