Overloading Iterator: C++ Semantics Question
Please note this code was not written by me. Otherwise I would not be asking this question. Full credit goes to Jerry Coffin. Anyways the code generates a sequence of numbers by overloading std::iterator< >.
I will first post the code, then I will give my interpretation of what I am seeing. If an expert C++ member could correct me if I am wrong I would greatly appreciate it.
Code
static const int N = 10;
template <class T>
class sequence : public std::iterator<std::forward_iterator_tag, T>
{
private:
T val;
public:
sequence(T init) : val(init) { }
T operator *( ) { return val; }
sequence &operator++( ) { ++val; return *this; }
bool operator != ( const sequence &other ) { return val != other.val; }
};
void foo( )
{
typedef std::vector<int> graph;
g开发者_Go百科raph id1( gen_seq(0), gen_seq( N ) );
display( id1 ); /* Not declared */
}
/* displays: 0 1 2 3 4 5 6 7 8 9 */
So when looking at this I see that you create a class which contains a value. Now we pass two of those to vector's constructor, which can takes two iterators. Now each time vector's constructor uses the ++ operator on the "sequence" it increments the value inside of the iterator. Technically, you could write:
graph id1( gen_seq( 0 ), gen_seq( 0 ) );
and this would generate the same sequence correct? Or is it the != operator that checks to make sure 0 has not gone to N. Any input on this would help greatly. I just finished reading Stroustrup's C++ Programming Language 3rd edition where he touched upon iterators, however inheriting from them was not a big topic and something I am not fully understanding. Kind of whish I would have done all his exercises, because I remember him asking to overload an iterator a few times.
You aren't really overloading an iterator; you're writing your own iterator, which involves overloading a few operators.
That std::vector
constructor effectively looks like this:
template <typename ForwardIterator>
vector(ForwardIterator first, ForwardIterator last)
{
for (ForwardIterator it = first; it != last; ++it)
push_back(*it);
}
(In reality, it's more complex than that because it needs to handle random-accessible ranges more efficiently, but for a forward iterable range, this is what it does.)
As you can see, that constructor performs three operations on your iterator: it dereferences it (*it
), it increments it (++it
), and it performs an inequality comparison (it != last
). Where it does each of these things, it calls the corresponding operator that you've defined in your custom iterator class.
graph id1( gen_seq( 0 ), gen_seq( 0 ) );
would work, but it would not give the same result: it would leave id1
empty. Based on the explanation in the previous paragraphs, do you see why?
No graph id1( gen_seq( 0 ), gen_seq( 0 ) );
would generate a zero size vector. Your second explanation using !=
is closer. Might help if you understood the vector ctor code. It is something like this
template <class II>
vector::vector(II first, II last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
As you can see !=
is being used to determine when to stop adding items to the vector.
(For various technical and efficiency reasons the actual vector ctor code is likely to be much more complex than this, but the above gives the essense as it applies to your case).
精彩评论