簡體   English   中英

為什么 std::list 上的 push_back 會更改用 rbegin 初始化的反向迭代器?

[英]Why does a push_back on an std::list change a reverse iterator initialized with rbegin?

根據我發現的一些 STL 文檔,在 std::list 中插入或刪除元素不會使迭代器無效。 這意味着它可以循環遍歷列表(從begin()end() ),然后使用 push_front 添加元素。

例如,在下面的代碼中,我用元素 a、b 和 c 初始化了一個列表,然后遍歷它並對元素執行 push_front。 結果應該是 cbaabc,這正是我得到的:

std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");

for (std::list<std::string>::iterator itList = testList.begin(); itList != testList.end(); ++itList)
   testList.push_front(*itList);

for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
   std::cout << *itList << std::endl;

當我使用反向迭代器(從rbegin()循環到rend() )並使用 push_back 時,我會期待類似的行為,即 abccba 的結果。 但是,我得到了不同的結果:

std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");

for (std::list<std::string>::reverse_iterator itList = testList.rbegin(); itList != testList.rend(); ++itList)
   testList.push_back(*itList);

for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
   std::cout << *itList << std::endl;

結果不是abccba ,而是abcccba 沒錯,多了一個 c。

看起來第一個 push_back 也改變了用 rbegin() 初始化的迭代器的值。 在 push_back 之后,它不再指向列表中的第三個元素(以前是最后一個),而是指向第四個元素(現在是最后一個)。

我使用 Visual Studio 2010 和 GCC 對此進行了測試,兩者都返回相同的結果。

這是一個錯誤嗎? 或者我不知道的反向迭代器的一些奇怪行為?

該標准規定迭代器和引用在插入期間保持有效。 它沒有說明任何關於反向迭代器的內容。 :-)

rbegin()返回的reverse_iterator在內部保存end()的值。 push_back()之后,這個值顯然與之前不同。 我認為標准沒有說明它應該是什么。 明顯的替代方案包括列表的前一個最后一個元素,或者如果它是固定值(如哨兵節點),則它留在最后。


技術細節: rend()返回的值不能指向begin()之前,因為那是無效的。 因此決定rend()應該包含begin()的值,並且所有其他反向迭代器進一步移動一個 position。 operator*對此進行補償並訪問正確的元素。

24.5.1 反向迭代器的第一段說:

Class 模板reverse_iterator是一個迭代器適配器,它從其底層迭代器定義的序列末尾迭代到該序列的開頭。 反向迭代器與其對應的迭代器 i 之間的基本關系由恆等式建立:
&*(reverse_iterator(i)) == &*(i - 1)

我認為要理解這一點,最好先將for循環重新轉換為while循環:

typedef std::list<std::string> container;

container testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");

container::reverse_iterator itList = testList.rbegin(); 
while (itList != testList.rend()) {
    testList.push_back(*itList);
     ++itList;
}

除此之外,我們還必須了解reverse_iterator通常是如何工作的。 具體來說, reverse_iterator確實指向在取消引用時獲得的元素之后的元素。 end()產生一個指向容器末尾之后的迭代器——但是對於像 arrays 這樣的東西,沒有定義的方法指向容器開始之前。 C++ 所做的是讓迭代器從結束之后開始,然后前進到開始,但是當您取消引用它時,您會在它實際指向的位置之前獲得元素。

這意味着您的代碼實際上是這樣工作的:

在此處輸入圖像描述

在那之后,你幾乎得到了你所期望的,推回 B,然后是 A,所以你最終得到了 ABCCCBA。

嘗試對兩者都使用迭代器。 嘗試:

std::list<std::string>::iterator i = testList.end(); 

並通過 --i 反轉

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM