2009-03-11 3 views
3

私は物事をやって、私はベクトルを通じて行進てる状況があります。イテレータを使用してstd :: vectorを歩き回り、気づかない最もクリーンな方法は何ですか?

 
std::vector::iterator iter = my_list.begin(); 

for (; iter != my_list.end(); ++iter) 
{ 
    if (iter->doStuff()) // returns true if successful, false o/w 
    { 
    // Keep going... 
    } 
    else 
    { 
    for (; iter != m_list.begin(); --iter) // ...This won't work... 
    { 
     iter->undoStuff(); 
    } 
    } 
} 

通常の条件下では - すべてがうまくいくと仮定すると - 私はmy_list.end()にすべての道を行進し、正常にループを終了します。

しかし、私が何かをやっている間に何かがうまくいかない場合、私はすべてを元に戻すことができるようにしたい - 基本的に私のステップをベクトルの最初に戻し、すべてを一度に逆順に取り消します。

私はmy_list.begin()(ネストされたforループに示されているように)になると、リストの最初の要素にまだundoStuff()を呼び出す必要があるため、実際にはまだ完了していません。さて、私はループの外で最後の呼び出しを行うことができますが、これは少し汚れているようです。

私はそれを見る方法は、私がmy_list.rend()になるときにのみ行われます。しかし、私はstd :: vector :: iteratorをstd :: vector :: reverse_iteratorと比較することはできません。

私がやろうとしていることを考えれば、イテレーター型/ループの組み合わせの最良の選択は何ですか?

答えて

4

うまくrbegin()rend()作品を経由して逆イテレータを使用している間、残念ながら私は、変換することを見つけます逆と逆のiterarotrsの間でかなり混乱する傾向があります。私は、変換の前後に増減する必要があるかどうかを問わず、ロジックパズルの演習を通過することなく、決して覚えていないことがあります。その結果、私は一般的に変換を避けます。

エラー処理ループをコード化する方法は次のとおりです。私はあなたがundoStuff()に失敗したイテレータを呼び出す必要はないと思います。結局のところ、doStuff()は成功しなかったと言いました。

// handle the situation where `doStuff() failed... 

// presumably you don't need to `undoStuff()` for the iterator that failed 
// if you do, I'd just add it right here before the loop: 
// 
//  iter->undoStuff(); 

while (iter != m_list.begin()) { 
    --iter; 
    iter->undoStuff(); 
} 
+0

イテレータを考える簡単な方法は、要素間の位置にあるカーソルです。前方参照イテレータは参照解除されたときにカーソルの後に要素を生成し、逆参照器は逆参照されるときにカーソルの前に要素を生成します。等価順方向反復子と逆方向反復子は、同じ位置にあるカーソルです。 RAIIの使用のために – Mankarse

8

STLベクトルについては少し錆びますが、最初のイテレータからstd::vector::reverse_iteratorを作成することは可能でしょうか?その後、先に行った最後の項目から開始するだけで、それをmy_list.rend()と比較して、最初の項目が処理されていることを確認できます。

+0

はい、あなたはそれを行うことができます。ここをクリックしてください:http://www.gamedev.net/community/forums/topic.asp?topic_id=388555 –

4

ベクトルを使用しない理由はもちろんありませんoperator[]()あなたのコードをより鮮明に、より簡単に、より効率的にするなら、もちろんです。

+0

ほとんどの場合、効率的ではありません。イテレータは、STLデータ型の抽象ポインタです。 []は、例えば、(STL)リンクリストの場合はひどく動作します。 – strager

+0

まあ、私は "if"と言っていました:-) –

+0

そして、std :: listに演算子があることに気づいていませんでした[ –

1

可逆イテレータを取得するには、rbegin()を使用する必要があります。

個人的に、私はまだreverse_iteratorを使用しない

for (int i=0;i<vecter.size();i++) { } 
2

を好む、あなたは後方この方法を歩くことができます。

while(iter-- != m_list.begin()) 
{ 
    iter->undoStuff(); 
} 

これはiterのコピーを作成しますが、コストが大きすぎてはなりません。あなたがより良いスピードをリファクタリングすることができます

while(iter != m_list.begin()) 
{ 
    --iter; 
    iter->undoStuff(); 
} 
+0

最初の要素に障害がある場合、whileループは決して入力されません。 – Runcible

+0

@Runcible、ああ、これは本当です。私はこれに気付かなかった。ごめんなさい。私はこの問題を解決するために私の答えを更新しようとします。 – strager

+0

@Runcible:doStuff()呼び出しに失敗した反復でundoStuff()を呼び出す必要がありますか?もちろん、これはメソッドの動作に依存しますが、しばしば失敗します(つまり、失敗したfopen()に対してfclose()を呼び出すことはありません)。 –

1

[OK]を、私はここに手足に出て行くよ..

std::vector iterator iter = my_list.begin(); 
bool error = false; 

while(iter != my_list.end()) 
{ 
    error = !iter->doStuff(); 
    if(error) 
    break 
    else 
    iter++; 
} 

if(error) 
do 
{ 
    iter->undoStuff(); 
    iter--; 
} 
while(iter != my_list.begin()) 
+0

多分私はこれを誤解しています - しかし、最初の要素にエラーがある場合、それはiterのようです - 2番目のループでは範囲外になり、Something Badをしますか? – Runcible

+0

私はあなたが正しいと思いますが、少なくともbegin()イテレータを減らすことは未定義です。あまりにも悪いですが、iter - をif(iter!= my_list.begin())iter--に置き換えることによって、醜い行を確実に横切るでしょう。 :) –

0

これは私が過剰性能と呼んでいるものですが、それはそれはあなたのdoStuff()機能が何をするかに依存し、どのように重要な性能は、あなたのコンテキストにある

// This also can be done with adaptators I think 
// Run DoStuff until it failed or the container is empty 
template <typename Iterator> 
Iterator DoMuchStuff(Iterator begin, Iterator end) { 
    Iterator it = begin; 
    for(; it != end; ++it) { 
    if(!*it->DoStuff()) { 
     return it; 
    } 
    } 
    return it; 
} 

// This can be replaced by adaptators 
template <typename Iterator> 
void UndoMuchStuff(Iterator begin, Iterator end) { 
    for(Iterator it = begin; it != end; ++it) { 
    it->UndoStuff(); 
    } 
} 

// Now it is so much easier to read what we really want to do 
typedef std::vector<MyObject*> MyList; 
typedef MyList::iterator Iterator; 
typedef MyList::reverse_iterator ReverseIterator; 
Iterator it = DoMuchStuff(my_list.begin(), my_list.end()); 
if(it != my_list.end()) { 
    // we need to unprocess [begin,it], ie including it 
    UndoMuchStuff(ReverseIterator(1+it), ReverseIterator(my_list.begin())); 
} 
2

とても楽しいです。可能であれば、ベクトルのコピーを扱うのはおそらくもっと分かりやすいでしょう(つまり、読者にとってはより簡単です)。すべてが正常であれば、ベクトルを入れ替えてください。

std::vector<Foo> workingCopy; 
workingCopy.assign(myVector.begin(), myVector.end()); 

bool success = true; 
auto iter = workingCopy.begin(); 
for(; iter != workingCopy.end() && success == true; ++iter) 
    success = iter->doStuff(); 

if(success) 
    myVector.swap(workingCopy); 
+0

+1! – Mankarse

+0

私は 'std :: vector'のコピーコンストラクタを使って' std :: vector workingCopy = myVector; 'と言うだけです。私はDoStuffスローを好むでしょう(何らかの複雑な操作を前提としていますが、途中で途切れてしまう何らかの深刻な呼び出しがある場合は例外を優先します)、または 'for(auto iter - > doStuff())はfalseを返します。iter = workingCopy.begin(); iter!= workingCopy.end(); ++ iter) } return true; ' これは、参照によってworkingCopyを実行する独自の関数である必要があります。その戻り値を使用して、スワップするかどうかを決定します。 –

0

これはreverse_iteratorで行うことができます。

bool shouldUndo(false); 
std::vector::iterator iter(my_list.begin()), end(my_list.end()); 
for (; iter != end && !shouldUndo; ++iter) 
{ 
    shouldUndo = iter->doStuff(); // returns true if successful, false o/w 
} 
if (shouldUndo) { 
    reverse_iterator<std::vector::iterator> riter(iter), rend(my_list.rend()); 
    //Does not call `undoStuff` on the object that failed to `doStuff` 
    for (; riter != rend; ++riter) 
    { 
    iter->undoStuff(); 
    } 
} 
関連する問題