2

と呼ばれる抽象的なコンテナのようなクラスがあるとしRuleBookます。のユーザーは、を取得するためRuleBookにを前方反復できることを期待しています。RuleBookRule

標準のコンテナとは異なり、ここでは具象サブクラスのメモリレイアウトに制限はありません。RuleBook代わりに、サブクラスの実装者は、独自のデータ構造に基づいて、の前方反復要件に準拠し、この要件を満たす必要があります。

私はそれRuleBookが純粋な仮想begin()を含むべきだと思っているので、範囲ベースend()ので動作するでしょうが、私はいくつかの問題に直面しています。

begin()とend()の署名はどうあるべきですか?BasketballRulesとCompanyRulesはどのように実装する必要がありますか?

イテレータが最後の項目を通過したときの終了条件をどのように処理しますか?

以下の例では、それを想定しm_rpm_rppそれぞれ1つのルールのみを指すことができます。内臓用のある種のイテレータ(のようなRule*)を返したいと思います。また、Fooのすべてのサブクラスには、さまざまなデータ構造が含まれていると想定できますRule。これは、実装者の気まぐれ次第です。

Rule*イテレータとして、またエンドポイントを超えて使用してすべてを実装する場合null_ptr、これはSTLに準拠しますか?

現在、カスタムイテレータを検討していますが、各サブクラスが反復の内臓を効果的に定義する必要があるため、この問題がそのパラダイムにうまく適合するかどうかさえわかりません。

コード

struct RuleBook
{
  // virtual Rule* begin() = 0;
  // virtual Rule* end() = 0; 
};

struct CompanyRules :
    public RuleBook
{
    Rule m_r;
};

struct BasketballRules :
    public RuleBook
{
    // return the three Rules, one-at-a-time, in succession
    Rule   m_r;
    Rule*  m_rp;
    Rule** m_rpp;
};

int
main( int argv, char* argc[] )
{
}
4

4 に答える 4

2

これを正しく行うのは難しいでしょう。

begin()とend()の署名はどうあるべきですか?

多くの選択肢はありません、彼らはほとんど次のようなものでなければなりません

RuleBook::iterator begin();
RuleBook::iterator end();

const(必要に応じてオーバーロードを追加します)

BasketballRulesとCompanyRulesはどのように実装する必要がありますか?

気をつけて :)

イテレータが最後の項目を通過したときの終了条件をどのように処理しますか?

イテレータタイプを正しく設計して、正しく機能するようにします。等しいかどうかを比較でき、インクリメントできるイテレータタイプが必要です。コンテナ内の最後のアイテムへのイテレータがあり、それをインクリメントする場合、それは過去のイテレータと等しくなる必要があります。

Rule *をイテレーターとして使用し、null_ptrをエンドポイントを超えて使用してすべてを実装する場合、これはSTLに準拠しますか?

いいえ。イテレータタイプがちょうどそのときイテレータRule*をインクリメントしても次の場所に移動しない場合はRule、メモリ内の次の場所を指しているだけであり、オブジェクトでさえない可能性があり、Rule未定義の動作につながります。たとえば、ポイントしているときにそれをインクリメントするBasketballRules場合、有効なオブジェクトをポイントしていません。つまり、aによって占有されているメモリをポイントしており、それを逆参照することは未定義の動作です。Rule*m_rRulem_rpRule*

また、aをインクリメントし続けるとRule*、過去の値に到達することはありませんnullptr

もっともらしい実装であるため、私はYakkの回答に賛成票を投じましたが、正しく理解するのは難しいでしょう。考慮し、ポリモーフィックインターフェイスに含めることはたくさんあります。たとえば、1つがaを指し、もう1つがaを指す2つのオブジェクト==を比較するために使用するとどうなりますか?ポリモーフィックイテレータの等式はどのように機能しますか?RuleBook::iteratorCompanyRulesBasketballRules

BasketballRulesイテレータをオブジェクトに、イテレータをオブジェクトに割り当てるとどうなりCompanyRulesますか?ポリモーフィックタイプには「ディープコピー」が必要です。

コンテナごとに異なる派生イテレータ-implタイプが必要です。コンテナのイテレータタイプは、派生コンテナタイプごとにタイプCompanyRuleに関するすべてを知っている必要があります。CompanyRuleこれらの具体的なイテレータ-implタイプのそれぞれは、仮想関数としてイテレータインターフェイスのほぼ全体を実装する必要があります。実装の難しさは、設計に問題があることを示しています。

より単純な設計は、派生したコンテナタイプごとに、同じタイプの実際の物理コンテナを管理することです。各派生コンテナに固有のコードは、派生オブジェクトのコンテンツが変更されるたびにリストのコンテンツを更新するだけで構成されています。その場合、イテレータタイプは単純で、非多型です。

struct RuleBook
{
  typedef std::vector<Rule*> list_type;
  typedef list_type::iterator iterator;

  virtual iterator begin() = 0;
  virtual iterator end() = 0;
};

struct CompanyRules :
    public RuleBook
{
    CompanyRules() : m_list{ &m_r } { }
    Rule m_r;

    iterator begin() { return m_list.begin(); }
    iterator end() { return m_list.end(); }

private:
    list_type m_list;
};

struct BasketballRules :
    public RuleBook
{
    BaseketballRules() : m_list{ &m_r, m_rp, *m_rpp } { }

    // return the three Rules, one-at-a-time, in succession
    Rule   m_r;
    Rule*  m_rp;
    Rule** m_rpp;

    iterator begin() { return m_list.begin(); }
    iterator end() { return m_list.end(); }

private:
    // N.B.
    // must update m_list[1] any time m_rp changes
    // must update m_list[2] any time the pointee of m_rpp changes (harder!)
    list_type m_list;
};
于 2012-12-30T13:20:41.797 に答える
1

範囲ベースのforループは使用される式で定義されるため、正確なシグニチャは重要ではありません。beginまたはendメンバー関数が見つかった場合、それらはandのように呼び出され__range.begin()ます__range.end().begin()署名が重要でない例としては、これらのメンバー関数は、 andのように呼び出すことができる限り、任意の数とタイプのパラメーターを持つことができます.end()(つまり、パラメーターにはデフォルト値が必要です)。

beginタイプにendメンバー関数がない場合は、式begin(__range)end(__range)が使用されます。(ここで、__rangeauto &&__rangerange-for-loopで使用する式で初期化されます)。したがって、引数に依存するルックアップが機能する限り、正確な署名は重要です。


BasketballRulesとCompanyRulesはどのように実装する必要がありますか?

イテレータが最後の項目を通過したときの終了条件をどのように処理しますか?

これらは、range-base-for-loopsがどのように機能するかとは別の質問です。具体的には、これらについて他の質問をする必要があります。

ただし、イテレータとしてポインタを使用する場合nullptr、最後の有効なポインタをインクリメントしてもnullポインタが得られないため、終了イテレータとして使用することは適切ではありません。範囲の終わりを1つ超えたポインタが表示されます。またRule*、イテレータとして使用する場合は、実装をコンテナクラスに任せる必要はありません。コンテナは、連続したルールの配列を維持する必要があります。

于 2012-12-30T09:07:53.413 に答える
1

Boostには、いくつかのイテレータヘルパーテンプレートクラスがあります。crtpでは、いくつかのメソッドを実装する必要があります。それらのpImplベースのユーザーは、イテレーターの準拠した仮想動作を許可します。つまり、pImplはイテレータが作業を委任する純粋な仮想抽象クラスです。

純粋な仮想pImplイテレータを作成します。beginこれがとの戻り型ですendpImpl子クラスは、データの格納方法に合わせてカスタム作成された具体的なインスタンスを使用して、イテレータのインスタンスを作成します。

于 2012-12-30T12:09:34.397 に答える
0

AlexAllainが範囲ベースのforループについてのチュートリアルでここで説明している方法のようなものを試すことができるかもしれません。また、範囲ベースのforループで反復可能なデータ構造を作成する例もあります。

必要なものは

  1. .begin()および.end()メソッド。スタンドアロンにすることもできます。
  2. operator!=、++、および*のオーバーロードにより、実行する必要のあるイテレータ操作がサポートされます。
于 2012-12-30T09:37:44.643 に答える