2

I need to create a function which receives the iterator from the begin and the end of one container. Then it should show the content in the console.

My problem is that i dont know how to declare the iterator so that it can work with any type of container

This is what I did:

template <class T>
void print(typename iterator<T> &beg, typename iterator<T> &end) {
    while (beg != end) {
        cout << *beg << endl;
        beg++;
    }
}
Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
Emiliano
  • 21
  • 1

2 Answers2

5

The std::iterator class is really just a convenience; there's nothing in the standard that requires all iterators to inherit from it. Additionally, std::iterator doesn't have virtual methods, so it's not nearly the same thing as taking an Iterator<T> in, say, Java, where invoking the next() method would call the appropriate next(). You want to take a general type T, not just an std::iterator, so that the compiler will resolve to the correct overloads of operator++ and operator* at compile-time.

template <typename T>
void print(T iter, const T& end) {
    // Taking the first argument by value ensures that
    // we don't modify the caller's variables
    while (iter != end) {
        cout << *iter << endl;
        ++iter;
    }
}

This will work for any forward iterators, which is what you're dealing with 99% of the time.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • 2
    A suggestion to simplify the code a little: `void print (T iter, const T & end)`. If you receive the first parameter as a modifiable copy, you can avoid the following `T iter = beg;`. – max66 Dec 31 '17 at 19:07
  • Thank you. And how would you call the function in main? for example if you want to use it with a vector or a list – Emiliano Dec 31 '17 at 19:11
  • Same as the way you intended to call yours. `print(vec.begin(), vec.end())`. The compiler should be able to infer the template types for you. – Silvio Mayolo Dec 31 '17 at 19:11
  • Yes, but I need to define the type of data in the angle brackets when calling the function. I mean, I need to call it as print (vec.begin (), vec.end ()) – Emiliano Dec 31 '17 at 19:21
  • `std::iterator` has also been deprecated in C++17. But why should it ever have been virtual in the first place? – Christian Hackl Dec 31 '17 at 19:23
  • 1
    @Emiliano: Why do you "need" to do that? – Christian Hackl Dec 31 '17 at 19:23
  • @Emiliano You very rarely need to explicitly define your template parameters when invoking a function, and in this case you certainly don't need to. – Silvio Mayolo Dec 31 '17 at 19:24
  • @ChristianHackl I was not insinuating that it should ever have been virtual. Simply clarifying that it is not, in case the OP comes from somewhere like Java where taking an `Iterator` would be fairly idiomatic. – Silvio Mayolo Dec 31 '17 at 19:25
  • 1
    @SilvioMayolo: I think your wording makes it sound as if its non-virtual destructor was one of the reasons not to inherit from it. I know now that you didn't mean it, but if your intention was to clarify things for beginners coming from Java, then you should reconsider the wording. – Christian Hackl Dec 31 '17 at 19:26
  • Upvoted because of the more detailed information about `std::iterator`'s general uselessness compared to a Java base class. – Christian Hackl Dec 31 '17 at 19:30
  • I had to reread some things because I was a bit confused, now I understood. Thank you very much @SilvioMayolo – Emiliano Dec 31 '17 at 19:41
3

I need to create a function which receives the iterator from the begin and the end of one container.

Look how standard functions do it, for example std::find:

template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );

Observations:

  • InputIt does not need to inherit from the (now obsolete) std::iterator class or any other class. Among other advantages, this allows the function to be used with an array.
  • The same iterator type is used for start and end.
  • The iterators are passed by value.
  • The template parameter does not specify the iterators' value type.

Just do it exactly like that in your own code and you'll be fine:

#include <iostream>
#include <vector>

template <class Iterator> // not T
void print(Iterator beg, Iterator end) {
    while (beg != end) {
        std::cout << *beg << '\n';
        beg++;
    }
}

int main() {
    std::vector<int> const vec = { 1, 2, 3 };
    int const array[] = { 1, 2, 3 };
    using std::begin;
    using std::end;
    print(begin(vec), end(vec));
    print(begin(array), end(array));
}
Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • Thank you very much for the observations and the example, they helped me a lot – Emiliano Dec 31 '17 at 19:46
  • I suggest to add an explanation why iterators generally are passed by value, even if the function doesn't modify them. A common pattern is to declare read-only parameters as *reference to const*, which most of the time isn't done in the case of iterators. I know the answer, other readers possibly not. – zett42 Dec 31 '17 at 21:17