2016-09-20 13 views
-1

イテレータの基本クラスといくつかの派生クラスを書くには?反復子継承と継承* this

イテレータはそれ自体(* this)を返す必要がありますか?

これまで、typename Xstatic_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

また、 'ContiguousIterator'は、2つの無関係な公共' Tを持っています* p'メンバーです 混沌。 –

+0

私はcrtpを知らなかった。この場合、crtpの助けになるでしょうか? – rxu

+0

簡単な修正:T * pを非公開に変更します。それを保護されたものに変更することはできません。いくつかの方法。私は前に試しました。 – rxu

答えて

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

ベースは、通常派生型をとり、それに加えて、テンプレートパラメータとして機能するために署名を必要とするものは何でも。次に、派生型を与える2つの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->を使用する必要が除いて、親がテンプレートであるため、その後、派生型は単に、通常通りに継承。ここで働く、それを参照してください。http://coliru.stacked-crooked.com/a/81182d994c7edea7

はこれは非常に効率的イテレータを生成し、オーバーヘッドなしで、私は、この技術はブーストのイテレータライブラリにはかなり頻繁に使用されていると信じて:http://www.boost.org/doc/libs/1_59_0/libs/iterator/doc/#new-style-iterators

+0

私の神。私は、仮想がイテレータに重大なオーバーヘッドを与えることは知らないだろう。 http://programmers.stackexchange.com/questions/191637/in-c-why-and-how-are-virtual-functions-slower答えに感謝します。 – rxu

+0

@rxu:あなたがベースを使用していないとすれば、virtualはおそらくあなたのケースにオーバーヘッドを追加しません。しかし、これは保証されていません。 –

+0

基本クラスの仮想デストラクタを宣言することをお勧めしますか?これによりオーバーヘッドが発生しますか? – rxu