[英]vector::erase and reverse_iterator
我在std :: vector中有一個元素集合,它們從第一個元素開始按降序排序。 我必須使用向量,因為我需要將元素放在連續的內存塊中。 我有一個集合,其中包含許多具有所述特征的向量實例(總是按降序排序)。
現在,有時,當我發現我在更大的集合中有太多元素(持有這些向量的元素)時,我丟棄這些向量中的最小元素,類似於這個偽代碼:
grand_collection: collection that holds these vectors
T: type argument of my vector
C: the type that is a member of T, that participates in the < comparison (this is what sorts data before they hit any of the vectors).
std::map<C, std::pair<T::const_reverse_iterator, std::vector<T>&>> what_to_delete;
iterate(it = grand_collection.begin() -> grand_collection.end())
{
iterate(vect_rit = it->rbegin() -> it->rend())
{
// ...
what_to_delete <- (vect_rit->C, pair(vect_rit, *it))
if (what_to_delete.size() > threshold)
what_to_delete.erase(what_to_delete.begin());
// ...
}
}
現在,在運行此代碼之后,在what_to_delete
我有一組迭代器指向我想從這些向量中移除的原始向量(總體最小值)。 請記住,原始向量在它們what_to_delete[0 - n]
此代碼之前進行排序,這意味着對於任何what_to_delete[0 - n]
,位置n - m
上的迭代器無法指向距離相同向量的開頭更遠的元素。 n
,其中m > 0
。
從原始向量中刪除元素時,我必須將reverse_iterator轉換為迭代器。 為此,我依賴於C ++ 11的§24.4.1/ 1:
reverse_iterator和迭代器之間的關系是&*(reverse_iterator(i))==&*(i-1)
這意味着要刪除vect_rit
,我使用:
vector.erase(--vect_rit.base());
現在,根據C ++ 11標准§23.3.6.5/3
:
迭代器擦除(const_iterator位置); 效果:在擦除點處或之后使迭代器和引用無效。
這如何與reverse_iterators一起使用? reverse_iterators是在內部實現的,是對向量的真實開始( vector[0]
)的引用,並將該vect_rit轉換為經典迭代器,那么擦除是否安全? 或者reverse_iterator使用rbegin()( vector[vector.size()]
)作為參考點並刪除任何遠離vector 0-index的東西仍會使我的反向迭代器無效?
編輯:
看起來像reverse_iterator使用rbegin()作為其參考點。 以我描述的方式擦除元素在刪除第一個元素后給出了關於非可引用迭代器的錯誤。 而在插入what_to_delete
正確地存儲經典迭代器(轉換為const_iterator
)時。
現在,為了將來參考,標准是否指定在隨機訪問reverse_iterator的情況下應該將哪些視為參考點? 或者這是一個實現細節?
謝謝!
從一個獨立的角度來看(我承認,我不是該標准的專家):從§24.5.1.1開始:
namespace std {
template <class Iterator>
class reverse_iterator ...
{
...
Iterator base() const; // explicit
...
protected:
Iterator current;
...
};
}
從§24.5.1.3.3開始:
Iterator base() const; // explicit
Returns: current.
因此,在我看來,只要你沒有在你的reverse_iterator
指向的內容之前刪除vector
任何內容, reverse_iterator
應保持有效。
當然,根據你的描述,有一個問題:如果你的向量中有兩個連續的元素,你最終想要刪除,你的vector.erase(--vector_rit.base())
意味着你已經使reverse_iterator
“指向”前一個元素無效,因此您的下一個vector.erase(...)
是未定義的行為。
為了防止這種情況顯而易見,讓我用不同的方式說:
std::vector<T> v=...;
...
// it_1 and it_2 are contiguous
std::vector<T>::reverse_iterator it_1=v.rend();
std::vector<T>::reverse_iterator it_2=it_1;
--it_2;
// Erase everything after it_1's pointee:
// convert from reverse_iterator to iterator
std::vector<T>::iterator tmp_it=it_1.base();
// but that points one too far in, so decrement;
--tmp_it;
// of course, now tmp_it points at it_2's base:
assert(tmp_it == it_2.base());
// perform erasure
v.erase(tmp_it); // invalidates all iterators pointing at or past *tmp_it
// (like, say it_2.base()...)
// now delete it_2's pointee:
std::vector<T>::iterator tmp_it_2=it_2.base(); // note, invalid iterator!
// undefined behavior:
--tmp_it_2;
v.erase(tmp_it_2);
在實踐中,我懷疑你會遇到兩種可能的實現:更常見的是,底層iterator
只是一個(適當包裝的)原始指針,所以一切都會很愉快地工作。 不太常見的是,迭代器實際上可能會嘗試跟蹤失效/執行邊界檢查(Dinkumware STL在調試模式下一次編譯時沒有這樣做嗎?),並且可能會對你大喊大叫。
在這個問題中,您已經准確引用了標准所說的reverse_iterator
:
reverse_iterator和迭代器之間的關系是&*(reverse_iterator(i))==&*(i-1)
請記住, reverse_iterator
只是底層迭代器( reverse_iterator::current
)之上的“適配器”。 正如你所說,對於reverse_iterator
來說,'參考點'是包裝的迭代器, current
。 reverse_iterator
上的所有操作都確實發生在底層迭代器上。 您可以使用reverse_iterator::base()
函數獲取該迭代器。
如果擦除--vect_rit.base()
,則實際上是擦除 - --current
,因此current
將無效。
作為旁注,表達式--vect_rit.base()
可能並不總是編譯。 如果迭代器實際上只是一個原始指針(可能是vector
的情況),那么vect_rit.base()
返回一個rvalue(以C ++ 11術語表示的prvalue),因此pre-decrement運算符將不起作用因為該運算符需要一個可修改的左值。 請參閱Scott Meyers的“有效STL”中的“第28項:了解如何使用reverse_iterator
的基礎iterator
”。 (該項目的早期版本可在http://www.drdobbs.com/three-guidelines-for-effective-iterator/184401406的 “准則3”中在線找到 )。
您可以使用偶數(++vect_rit).base()
表達式(++vect_rit).base()
來避免該問題。 或者因為你正在處理向量和隨機訪問迭代器: vect_rit.base() - 1
無論哪種方式, vect_rit
都會使vect_rit
無效,因為vect_rit.current
無效。
但是,請記住, vector::erase()
會將有效迭代器返回到剛剛擦除的元素的新位置。 您可以使用它來“重新同步” vect_rit
:
vect_rit = vector_type::reverse_iterator( vector.erase(vect_rit.base() - 1));
reverse_iterator
與正常iterator
,指向向量中的某個位置。 實現細節是無關緊要的,但是如果你必須知道,它們(在一個典型的實現中)都只是里面的普通舊指針。 不同的是方向。 反向迭代器有+
和-
反轉常規迭代器(以及++
和--
, >
和<
etc)。
這很有趣,但並不能真正暗示對主要問題的回答。
如果您仔細閱讀該語言,它會說:
在擦除點處或之后使迭代器和引用無效。
參考文獻沒有內置的方向感。 因此,語言清楚地指的是容器自身的方向感。 擦除點之后的位置是具有較高索引的位置。 因此,迭代器的方向在這里是無關緊要的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.