osmium said:
desktop said:
I am not sure I quite understand the use of iterators. I have this int
array:
int a[] = {1,2,3,4,5}
I would now like to make an iterator to the first and last element:
std::iterator<int> iter;
That construct is for the STL and arrays are not part of the STL. If you
want to implement the *properties* of an iterator, go ahead and write one.
But you can't use the one from STL.
Ok so if I want a generic function that works correctly on both int
arrays and vectors, I have to write a new iterator?
No, you make a parametrized function.
How do I check if an iterator has the right type?
An iterator is not a type, it's a concept. The compiler checks so that
the type you provided fulfils the requirements of the concept.
I have read that you don't declare an iterator type like Forward, its up
to the function that you implement to make sure that it will satisfy the
properties of a Forward iterator.
The compiler does.
That means that it is only by inspecting a function you can see which
iterators are supported. The compiler will not generate any error if I
have an iterator "iter" and then type:
--iter
because you cannot declare a Forward iterator explicitly. Or am I
missing something?
The compiler will refuse to compile the code if the iterator you
supplied does not support the -- operation.
Iterators is an abstract concept that lives in the minds of the
programmers, there is no one type that is an iterator or from which all
iterators inherits. When using iterators the compiler checks so that the
type used as an iterator provides all the functionality required and
will fail to compile the program if it does not. As long as the type
supplied do provide the functionality it *is* an iterator, regardless of
what type it has.
Consider the following code:
#include <numeric>
#include <iostream>
#include <vector>
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
int arrSum = std::accumulate(arr, arr + 5, 0);
int vecSum = std::accumulate(vec.begin(), vec.end(), 0);
std::cout << "Array: " << arrSum << std::endl;
std::cout << "Vector: " << vecSum << std::endl;
}
Here we call the same function (std::accumulate) with two different
types of iterators, in the first we use int* as iterator and in the
second std::vector<int>::iterator. Those two types have nothing in
common, except that parts of their interfaces are the same: you can
dereference them by using *, you can move them one step forward using
++, and so on. And since they both have the "right" functionality in
their interface we can use them as iterators.
Perhaps it will be easier to understand if I show you how accumulate is
implemented (or rather how I would implement it):
template<class Iter, class Val>
Val accumulate(Iter begin, Iter end, Val v)
{
while (begin != end)
{
v += *begin;
++begin;
}
return v;
}
From this you can see that accumulate places three requirements on the
type used as iterator, you shall be able to test two instance of the
type for inequality (!=), you shall be able to dereference an instance
of the type (*), and you shall be able to increment an instance (++). It
also places one requirement on the type you accumulate the values of,
you shall be able to add-assign (+=).