web-dev-qa-db-ja.com

逆イテレータを順イテレータに変換できますか?

Actionというクラスがあります。これは、本質的にMoveオブジェクトの両端キューのラッパーです。

Movesの両端キューを前後にトラバースする必要があるため、クラスのメンバー変数としてフォワードイテレーターとリバースイテレーターがあります。この理由は、dequeの "end"を1つ過ぎたとき、前進または後退するときを知る必要があるためです。

クラスは次のようになります。

class Action
{
public:
    SetMoves(std::deque<Move> & dmoves) { _moves = dmoves; }
    void Advance();
    bool Finished() 
    {
        if( bForward )
            return (currentfwd==_moves.end());
        else
            return (currentbck==_moves.rend());
    }
private:
    std::deque<Move> _moves;
    std::deque<Move>::const_iterator currentfwd;
    std::deque<Move>::const_reverse_iterator currentbck;
    bool bForward;
};

Advance関数は次のとおりです。

void Action::Advance
{
    if( bForward)
        currentfwd++;
    else
        currentbck++;
}

私の問題は、現在のMoveオブジェクトへのイテレータを取得できるようにしたいことです。前方に行くのか後方に行くのかを問い合わせる必要はありません。これは、1つのタイプの反復子を返す1つの関数を意味しますが、2つのタイプがあります。

イテレータを返すことを忘れて、代わりにMoveオブジェクトへのconst参照を返す必要がありますか?

ご多幸を祈る、

ビーバンド

43
BeeBand

これは、正確に STLの設計を開始するように促した問題の一種です。次の理由があります。

  1. コンテナとともにイテレータを保存しない
  2. 任意のイテレータを受け入れるアルゴリズムを使用する
  3. 一度に1つの項目ではなく範囲全体をアルゴリズムで評価する

あなたが今見ているものは、多かれ少なかれ実際の問題の氷山の一角だと思います。私のアドバイスは、一歩下がって、現在の設計の詳細に対処する方法を尋ねるのではなく、あなたが何を達成しようとしているのか、それをどのように達成するのが最善かについてより一般的な質問をすることです最終結果。

主にタイトルの質問に関心がある人にとっては、答えは非常に適格な「はい」です。特に、reverse_iteratorには、そのためのbase()メンバーがあります。ただし、資格には多少問題があります。

問題を実証するには、次のようなコードを検討してください。

_#include <iostream>
#include <vector>
#include <iterator>

int main() { 
    int i[] = { 1, 2, 3, 4};
    std::vector<int> numbers(i, i+4);

    std::cout << *numbers.rbegin() << "\n";
    std::cout << *numbers.rbegin().base() << "\n";
    std::cout << *(numbers.rbegin()+1).base() << "\n";

    std::cout << *numbers.rend() << "\n";
    std::cout << *numbers.rend().base() << "\n";
    std::cout << *(numbers.rend()+1).base() << "\n";
}
_

特定のマシンでこの特定の瞬間にこれを実行すると、次の出力が生成されます。

_4
0
4
-1879048016
1
-1879048016
_

要約:rbegin()must前方イテレータに変換する前に1つ追加して有効なイテレータを取得します-rend()not変換する前に1つ追加して、有効なイテレーターを取得します。

X.rbegin()X.rend()を汎用アルゴリズムのパラメーターとして使用している限り、それは問題ありませんが、経験から、フォワードイテレーターへの変換は多くの場合問題につながることを示しています。

ただし、最終的には、質問の本文(タイトルではなく)については、答えは上記のとおりです。問題は、コレクションをいくつかの反復子と組み合わせてそのコレクションに入れるオブジェクトを作成しようとすることに起因します。 。この問題を修正すると、順方向および逆方向の反復子を使用するビジネス全体が無意味になります。

27
Jerry Coffin

逆反復子には、対応する前方反復子を返すメンバー base() があります。これはis n'tが同じオブジェクトを参照するイテレータであることに注意してください-実際にはシーケンス内の次のオブジェクトを参照します。これにより、rbegin()end()に対応し、rend()begin()に対応します。

したがって、イテレータを返したい場合は、次のようにします

std::deque<Move>::const_iterator Current() const
{
    if (forward)
        return currentfwd;
    else
        return (currentbck+1).base();
}

ただし、参照を返し、繰り返しの詳細をすべてクラス内にカプセル化することをお勧めします。

65
Mike Seymour

std::dequeランダムアクセスコンテナーstd::vectorと同じ)であるため、両方のトラバーサルの両端キューに単一の整数インデックスを使用する方がはるかに優れています。

6

実際、同じクラスで2つの異なる動作をしているようです。

特に、コレクションは1つの順序でしかトラバースできないようです。そうでない場合、トラバースを開始してからbforward引数を変更すると、非常に奇妙な状況に陥ります。

個人的には、両方のイテレータを公開する(つまり、begin, end, rbegin and rendを転送する)ことはすべて私です。

単純なIteratorオブジェクトを返すこともできます。

template <class T>
class Iterator
{
public:
  typedef typename T::reference_type reference_type;

  Iterator(T it, T end) : m_it(it), m_end(end) {}

  operator bool() const { return m_it != m_end; }

  reference_type operator*() const { return *m_it; }

  Iterator& operator++() { ++m_it; return *this; }

private:
  T m_it;
  T m_end;
};

template <class T>
Iterator<T> make_iterator(T it, T end) { return Iterator<T>(it,end); }

次に、この単純なオブジェクトを返すことができます。

class Action
{
public:
  Action(std::deque<Move> const& d): m_deque(d) {} // const& please

  typedef Iterator< std::deque<Move>::iterator > forward_iterator_type;
  typedef Iterator< std::deque<Move>::reverse_iterator > backward_iterator_type;

  forward_iterator_type forward_iterator()
  {
    return make_iterator(m_deque.begin(), m_deque.end());
  }

  backward_iterator_type backward_iterator()
  {
    return make_iterator(m_deque.rbegin(), m_deque.rend());
  }


private:
  std::deque<Move> m_deque;
};

または、順方向と逆方向のトラバーサルを動的に選択する場合は、Iteratorを純粋な仮想インターフェースにし、順方向と逆方向の両方のトラバーサルを使用できます。

しかし、実際には、1つだけを使用するように見える場合、前方および後方のイテレータの両方を保存するポイントは本当にありません:/

1
Matthieu M.

たぶん、コンテナの選択を再考する必要があります。

通常、逆方向のイテレータを使用する必要はありませんが、

currentfwd--

デキューではうまくいかないかもしれませんが(すべて試しましたが)、逆方向に移動します。

本当にすべきなのは、ここでクラスをデキューのデコレータとしてモデル化し、独自のアクションイテレータを実装することです。とにかくそれは私がやることだろう。

0
Charles