-1

How to write a base class and several derived classes of iterator?

Does the iterator have to return itself (*this)?

So far, I use typename X and static_cast<X&>(*this) to allow the derived class to inherit a function that return itself from the base class.

This looks ugly. Is there a better way?

Simplified Code:

#include <iterator>
#include <iostream>
template <typename T, typename X>
class BaseIterator : public std::iterator<std::input_iterator_tag, T> {
    //Not intended to be used directly.
    private:
        T* p;
    protected:
        virtual void increment(void)=0;
        virtual T* stride_index(int index)=0;
    public:
        virtual ~BaseIterator(){} //virtual destructor.
        X operator++(int) { //takes a dummy int argument
            X tmp(static_cast<X&>(*this) );
            increment();
            return tmp;
        }
        bool operator==(const X & rhs) { return p==rhs.p; }
} ;
template <typename T>
class ContiguousIterator : public BaseIterator<T, ContiguousIterator<T> > {
    private:
        T* p;
    protected:
        inline void increment(void) {++p;}
        inline T* stride_index(int index){return p + index;}
    public:
        virtual ~ContiguousIterator(){} //destructor.
        ContiguousIterator(T* x) :p(x) {}
        ContiguousIterator(const ContiguousIterator<T> & mit) : p(mit.p) {}
} ;

int main(void){
    int i[]={0,1,2,3,4,5};
    ContiguousIterator<int> itbegin(i);
    ContiguousIterator<int> it(i);
    it++;
    std::cout << "result: " << (it == itbegin) << std::endl;
}

The alternative is to forget about using inheritance to write less code. Just copy and paste the function that return *this to the derived classes.

That alternative seems increasingly acceptable to me ...

Jarod42
  • 203,559
  • 14
  • 181
  • 302
rxu
  • 1,369
  • 1
  • 11
  • 29
  • 1
    Also, `ContiguousIterator` has two unrelated public `T* p` members. That's just chaos. – Mooing Duck Sep 20 '16 at 21:49
  • I didn't know crtp. Would crtp help in this case? – rxu Sep 20 '16 at 21:49
  • simple fix: change T* p to private. Can't change it to protected. some how. I tried before. – rxu Sep 20 '16 at 21:55
  • No solution after 25 views. why? Is inheritance of iterator simply a bad idea? – rxu Sep 20 '16 at 21:58
  • 1
    Because it's not clear why you're doing what you're doing, it's a lot of code with a poor explanation, and the "obvious" solution requires a lot of tests and writing for me to make it right. But: Done. – Mooing Duck Sep 20 '16 at 22:07

1 Answers1

1

Generally, virtual is a lot of overhead for something like iterators which ought to be lightweight. The usual way to go is CRTP. Which is a little tricky, but looks like this:

template <typename T, typename X>
class BaseIterator : public std::iterator<std::input_iterator_tag, T> {
    protected:
        T* p;
        X* self() {return static_cast<X*>(this);}
        const X* self() const {return static_cast<const X*>(this);}
        BaseIterator(T* x) :p(x) {}
    public:
        X operator++(int) { //takes a dummy int argument
            X tmp(*self());
            self()->increment();
            return tmp;
        }
        bool operator==(const X & rhs) const { return p==rhs.p; }
} ;

The base usually takes the derived type, plus whatever it needs for function signatures as template parameters. Then you add two self() functions, which give you the derived type, which means you don't actually need virtual. Everything is trivially inlined by the compiler. (Note I've also given it a sane constructor, and made operator== const.

template <typename T>
class ContiguousIterator : public BaseIterator<T, ContiguousIterator<T> > {
    public:
        typedef BaseIterator<T, ContiguousIterator<T> > parent;
        void increment(void) {++(this->p);}
        T* stride_index(int index) const {return this->p + index;}
    public:
        virtual ~ContiguousIterator(){} //destructor.
        ContiguousIterator(T* x) :parent(x) {}
        ContiguousIterator(const ContiguousIterator<T> & mit) : parent(mit.p) {}
} ;

Then the derived type simply inherits as normal, except you have to use this-> to access the member, since the parent is a template. See it working here: http://coliru.stacked-crooked.com/a/81182d994c7edea7

This produces a highly efficient iterator, with no overhead. I believe this technique is used quite heavily in Boost's iterator library: http://www.boost.org/doc/libs/1_59_0/libs/iterator/doc/#new-style-iterators

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • My god. I wouldn't know virtual give significant overhead for iterators. http://programmers.stackexchange.com/questions/191637/in-c-why-and-how-are-virtual-functions-slower Thanks a lot for the answer. – rxu Sep 20 '16 at 22:11
  • @rxu: Given that you don't use the base, virtual probably doesn't add any overhead for your case. But this guaranteed doesn't. – Mooing Duck Sep 20 '16 at 22:31
  • Is it a good idea to declare a virtual destructor for the base class? Will this create any overhead? – rxu Sep 21 '16 at 15:31
  • Hmm. The virtual destructor of the base class will also be called: http://stackoverflow.com/questions/2182925/virtual-destructor But if the virtual destructor of the base class does nothing. Is that ok? – rxu Sep 21 '16 at 15:51
  • @rxu: Declaring a virtual destructor does add memory overhead, and if your code has a `BaseIterator>` pointer or reference, then the code will probably have some time overhead. But I cant imagine why you would have one. And without it, there's no benefit to a virtual destructor. – Mooing Duck Sep 21 '16 at 17:13