반복자를 사용하여 std :: 벡터를 걷고 걸어 다니는 가장 깨끗한 방법은 무엇입니까?
문제
벡터를 통해 행진하고 일을하는 상황이 있습니다.
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()
- 루프를 위해 중첩 된대로 - 아직 전화해야하기 때문에 아직 완료되지 않았습니다. 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()));
}
이것은 a로 수행 할 수 있습니다 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();
}
}