イテレータを使用して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()
までずっと行進し、ループを正常に終了します。
ただし、作業中に何か問題が発生した場合は、すべてを元に戻したいと思います。基本的には、ベクターの最初に戻って、すべてを一度に1つずつ逆の順序で元に戻します。
私の問題は、ネストされたforループに示されているように my_list.begin()
に到達したとき- undoStuff(を呼び出す必要があるため、まだ完了していません。 )
リストの最初の要素に。これで、ループの外で最後の呼び出しを行うことができましたが、これは少し汚れているようです。
見方では、 my_list.rend()
に到達したときだけ完了です。ただし、std :: vector :: iteratorとstd :: vector :: reverse_iteratorを比較することはできません。
私がやろうとしていることを考えると、イテレータ型/ループの組み合わせの最良の選択は何ですか?
解決
rbegin()
および rend()
を介して逆イテレータを使用するとうまくいきますが、残念ながら逆イテレータと非逆イテレータの間の変換は非常に混乱する傾向があります。変換の前または後に論理パズルのエクササイズを行う必要があります。その結果、通常は変換を回避します。
ここに、おそらくエラー処理ループをコーディングする方法を示します。失敗したイテレータに対して 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();
}
他のヒント
STLベクトルに関してはさびていますが、初期イテレータから std :: vector :: reverse_iterator
を作成することは可能でしょうか?次に、先に進むときの最後のアイテムから開始するだけで、 my_list.rend()
と比較して、最初のアイテムが処理されていることを確認できます。
もちろん、ベクトルを使用しない理由はありません。 operator []()
を使用すると、コードがより明確、簡単、効率的になります。
これは、 doStuff()
関数が何をするか、およびコンテキストでのパフォーマンスの重要性に依存します。可能であれば、おそらくベクターのコピーで作業する方が明確(つまり、読みやすい)で、すべてが問題ない場合にのみ、ベクターを交換します。
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);
reverse_iterator
を使用せずに、このように逆方向に歩くことができます:
while(iter-- != m_list.begin())
{
iter->undoStuff();
}
これにより iter
のコピーが作成されますが、コストはそれほど大きくないはずです。より高速にリファクタリングできます:
while(iter != m_list.begin())
{
--iter;
iter->undoStuff();
}
可逆イテレータを取得するには、rbegin()を使用する必要があります。
個人的にはまだ好きです
for (int i=0;i<vecter.size();i++) { }
さて、ここで手足に出かけます。
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())
これは私がエンジニアリングと呼ぶものですが、とても楽しいです
// 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()));
}
これは、 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();
}
}