Итератор Наследование и наследование *this

Как написать базовый класс и несколько производных классов итератора?

Должен ли итератор возвращать себя (*this)?

Пока что пользуюсь typename X а также static_cast<X&>(*this) чтобы позволить производному классу наследовать функцию, которая возвращает себя из базового класса.

This выглядит некрасиво Есть ли способ лучше?

Упрощенный код:

#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;
}

Альтернатива - забыть об использовании наследования для написания меньшего количества кода. Просто скопируйте и вставьте функцию, которая возвращает *this к производным классам.

Эта альтернатива кажется мне все более приемлемой...

1 ответ

Решение

В общем-то, virtual это много накладных расходов для чего-то вроде итераторов, которые должны быть легкими. Обычный путь - это CRTP. Это немного сложно, но выглядит так:

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; }
} ;

База обычно принимает производный тип, плюс все, что нужно для сигнатур функций в качестве параметров шаблона. Затем вы добавляете два self() функции, которые дают вам производный тип, что означает, что вам на самом деле не нужно virtual, Все тривиально встроено компилятором. (Обратите внимание, я также дал ему вменяемый конструктор, и сделал 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) {}
} ;

Тогда производный тип просто наследует как обычно, за исключением того, что вы должны использовать this-> чтобы получить доступ к члену, так как родитель является шаблоном. Посмотреть это работает здесь: /questions/2428915/chto-takoe-horoshij-legkij-python-mvc-frejmvork/2428937#2428937

Это создает высокоэффективный итератор без дополнительных затрат. Я считаю, что этот метод довольно интенсивно используется в библиотеке итераторов Boost: http://www.boost.org/doc/libs/1_59_0/libs/iterator/doc/

Другие вопросы по тегам