2

私はインターフェイスにプログラミングしており、その内容を一般化された方法でやり取りしたいと考えています。したがって、一般的に、私のインターフェイスには次のようなプロトタイプがあります。

class Interface
{
public:
  class Iterator;
  virtual Interface::Iterator* begin() = 0;
  virtual Interface::Iterator* end() = 0;  

  class Iterator
  {
  public:
    virtual const Iterator* operator++() = 0;
    virtual bool operator!=(const Iterator& i) = 0;
  };
};

以下は、単純な特殊化の例です。

class Derived : public Interface
{
public:
  Derived() : array {2, 0, 1, 5, 4, 3} {};
  Iterator* begin() { return new Derived::IterDerived(0);};
  Iterator* end() { return new Derived::IterDerived(6);};

  int array[6];

public:
  class IterDerived : public Interface::Iterator
  {
  public:
    IterDerived(int i) {pos = i;};
    IterDerived(IterDerived&& i) {pos = i.pos;};

    const Interface::Iterator* operator++() override { ++pos; return this;};
    bool operator!=(const Interface::Iterator& i) { return pos != dynamic_cast<const     Derived::IterDerived&>(i).pos;};
    int position() { return pos;};

  private:
    int pos;
  };
};

ここまでは順調ですね。ここで、新しい標準 (c++11) で指定されているように、新しい for 範囲を使用してコンテンツを反復処理できるコードを書きたいと思います。実際のインターフェイスでは getter メソッドを提供するので、dynamic_cast を使用する必要はありません。すなわち:

int main()
{
  Interface *a = new Derived();

  for(auto i : a) 
    std::cout << dynamic_cast<Derived*>(a)->array[dynamic_cast<Derived::IterDerived*>(i.get())->position()] << " ";
  std::cout << std::endl;
}

しかし、このコードはコンパイルされません。コンパイルするには、次の形式でマクロを使用する必要があります。

#define FOR_ITERATOR(iter, object)                  \
  for(std::unique_ptr<Interface::Iterator> iter((object)->begin()), \
    iter##end((object)->end());                 \
      *iter != *(iter##end); ++(*iter))

for 範囲の場所。ステートメントに新しい範囲を使用する機会はありますか? または、この種のマクロを使用する必要がありますか?

4

2 に答える 2

6

あなたが言う時:

Interface *a = new Derived();
for(auto i : a) 

aはポインターであり、ポインターを反復処理することは実際には意味がありません。あなたはおそらく欲しい:

for (auto i : *a)

つまり、a指しているオブジェクトを反復処理します。

Interface::beginInterface::endメソッドが のInterface::Iterator *代わりに を返すため、それはおそらくまだ機能しませんInterface::Iterator。これはナンセンスです。

Interface::Iteratorのサブクラスを使用して派生クラスを反復処理できるようにしたいようですが、それが構築するオブジェクト (および関連する反復子)autoの静的型を把握できる必要があるため、機能しません。i

このようなことをしたい場合は、反復子クラス自体を固定したまま、Derived によってサブクラス化できるクラスへのポインターを反復子オブジェクトにカプセル化する必要があります。何かのようなもの:

class Interface
{
public:
  class Iterator;
  virtual Interface::Iterator begin() = 0;
  virtual Interface::Iterator end() = 0;  

protected:
  class IteratorImpl {
  public:
    virtual IteratorImpl &operator++() = 0;
    virtual bool operator!=(const Iterator& i) = 0;
    virtual IteratorImpl *clone() = 0;
  };
};

public:
  class Iterator {
  protected:
    class IteratorImpl *impl;
  public:
    Iterator(IteratorImpl *a) : impl(a) {}
    Iterator(iterator &a) : impl(a.impl->clone()) {}
    Iterator &operator++() { ++*rep; return this; }
        :

次に、これから次のように派生します

class Derived : Interface {
  class IteratorImpl : public Interface::IteratorImpl {
       :
  };
public:
  Iterator begin() { return Iterator(new IteratorImpl( ...

もちろん、必要なデストラクタ、代入演算子、逆参照演算子などをすべて追加する必要があります。

これはおそらく、実際に反復しているものが何であれ、含まれているはずの型に対してテンプレート化する必要があります。

于 2013-06-10T01:20:29.827 に答える
3

::Iterator は抽象オブジェクトであり、そうである必要があります。したがって、具体的なオブジェクトを返すことはできません。

次に、仮想 (実行時) ポリモーフィズムと汎用(コンパイル時) ポリモーフィズムの間で競合が発生します。範囲ベースの for および存在するほぼすべてのアルゴリズムは、イテレータに具象オブジェクトを使用します。それらは、アルゴリズムによって放棄されてコピーされます。

これを機能させるには、イテレータが仮想マシンを内部に隠す必要があります。Iteratorこれは、すべての呼び出しを転送する「実際の」イテレータ クラスへのポインタを格納する基本クラスを作成するのと同じくらい簡単です。

次のようにします。

class IteratorBase
{
public:
  virtual const IteratorBase* operator++() = 0;
  virtual bool operator!=(const IteratorBase& i) = 0;
};

class Iterator
{
public:
  explicit Iterator(IteratorBase *it) : m_it(it) {}
  const Iterator &operator++() {return Iterator(++(*m_it));}
  bool operator !=(const Iterator &i) {return *m_it != *i.m_it;}

private:
  IteratorBase *m_it;
};

からイテレータを派生させますIteratorBase

ジェネリック プログラミング スタイルのインターフェイスは、一般にポインターを避ける傾向があります。実際のオブジェクトではない場合、それらは参照でより機能します。したがって、クラスがジェネリック プログラミングで適切に機能するようにするには、その規則に従って許可する必要があります。そして、一般的に言えば、実行時のポリモーフィック範囲を持たせようとするのは非常に奇妙です。

ポインターは範囲ではありません。具体的な型への参照のみが範囲になることができます。したがって、 andforを特殊化しない限り、range-based にポインターを渡すことはできません。std::beginstd::end

于 2013-06-10T01:41:37.217 に答える