2013-10-17 108 views
5

在C++ 11的std::map,是有一些有效的迭代X使得++ X是保證等於map::begin()?我想檢測一個我剛剛調用的函數(我的)是否已經從函數的前面走過一個迭代器。該函數會將迭代器向後移動一個位置。STL迭代::開始()

答案是否適用於圖書館的其他部分?

+1

簡答:不需要。您真的需要找到其他方式來處理(或更好地防止)情況。 –

+0

@JerryCoffin這就是爲什麼我們有逆向迭代器,請參閱我的回答 – TemplateRex

回答

4

不,在std容器開始之前的迭代器都是UB(除了反向迭代器,它可能無法解決您的問題LEM)。

您可能需要修復有問題的功能。如果沒有,請在打電話之前將其包裝並抓住不良行爲。如果不這樣做,可以在map鍵類型順序中插入負無窮大元素,並添加一個有序值。如果不這樣做,你可以編寫迭代器適配器,將你的map迭代器包裝成可以在沒有UB的情況下開始一次性迭代器。

這些按我的推薦順序大致排序。每種方法都有可能失敗,並且隨着我的建議變得更加偏僻,它們會變得更加容易出錯和危險。

+0

迭代器包裝*乍一看似乎很乾淨,然後我考慮如何使用它們,並且非常快速地變得非常討厭。 – thirtythreeforty

+1

@thirtythreeforty yep,這就是爲什麼我把它包括在內,但只是作爲一個遙遠的「哦,我的上帝,沒有其他人會工作」的選擇。使用boost迭代器fascade可以幫助它只會適度地討厭。或者手寫。或者懶惰的concatinating兩個boost-type-erased迭代器範圍。 (再次按推薦順序)。如果你拿走最後一個,你會得到你應得的東西:它在理論上是有效的,但是會產生氣味。簡而言之,只需修復該函數,它不應該遞減它沒有有效範圍的迭代器。 – Yakk

+2

ahem,'std :: forward_list'確實有'before_begin()'成員 – TemplateRex

1

通過「走的迭代器關閉前」我猜你是遞減前向迭代是這樣的:

// don't do this: 
for(it = mymap.end(); --it >= mymap.begin();) { ... } 

相反,增加這樣一個反向迭代:

// this is better: 
for(it = mymap.rbegin(); it != mymap.rend(); ++it) { ... } 

-Jesse

+0

如果我使用逆向迭代器,我與另一個函數有同樣的問題,但是'map'的末尾和移動iterator * forward *。 – thirtythreeforty

+0

出於好奇,爲什麼你需要將迭代器移動到與其自然方向相反的位置?那麼**做{...} while(it!= mymap.begin(); ** –

+0

)我正在實現另一個迭代器,它必須圍繞我正在寫的地圖樹進行迭代;'ForwardIterator'正常工作;現在 – thirtythreeforty

3

認識到標準庫容器是半開放範圍[begin, end)是非常重要的,也就是說你可以迭代一個過去的結束。對於雙向(和隨機)迭代器,你也可以做--end()並從邊緣回來。由*end()解引用一個末尾是未定義的行爲,因此將開始迭代器遞減--begin()begin() - 1。這隻有一個例外:std::forward_list其中有一個不可解除引用的迭代器before_begin(),它滿足++before_begin() == begin()(但請注意,對於forward_list,您不能將begin()減1)。

雙向迭代器的這種基本不對稱意味着反向迭代器是常規迭代器周圍的精簡包裝器。在大多數標準庫實現中,它們只包含underyling迭代器的副本base_。遞增std::reverse_iterator稱之爲類似--base_; return *this;,並且取消引用 它確實auto old = base_; return *--old;。底層迭代器在任何時候都不會減少到begin()之前,並且不會通過這種方式去引用end()

以下是在四種方式來迭代一個容器支持雙向或隨機迭代器,和各種迭代器之間的關係(.base()轉換一個std::reverse_iterator回到其底層迭代器)

#include <iomanip> 
#include <iostream> 
#include <iterator> 
#include <map> 
#include <string> 

int main() 
{  
    auto c = std::map<int, std::string>{ {1, "hello"}, {2, "world"} }; 

    { // 1) forward iteratation 
     auto it = begin(c); 
     for (; it != end(c); ++it){} 
     std::cout << std::boolalpha << (it == c.rbegin().base()) << "\n"; 
    } 

    { // 2) meh, backward iteration 
     auto it = end(c); 
     for (; it != begin(c); --it){} 
     std::cout << std::boolalpha << (it == c.rend().base()) << "\n"; 
    } 

    { // 2') better: reverse iteration 
     auto it = c.rbegin(); 
     for (; it != c.rend(); ++it){} 
     std::cout << std::boolalpha << (it.base() == begin(c)) << "\n"; 
    } 

    { // 1') backward reverse, better avoid this 
     auto it = c.rend(); 
     for (; it != c.rbegin(); --it){} 
     std::cout << std::boolalpha << (it.base() == end(c)) << "\n"; 
    } 
} 

Live Example

如果您的數據結構應該支持雙向迭代,但沒有成員迭代器.rbegin()rend(),您可以通過std::reverse_iterator(end())std::reverse_iterator(begin())(這也是標準庫通常實現它們的方式)。

+0

因此,感謝從我的答案downquot垃圾,我只想說,我在答案中說,這是UB和UB不是魔鬼,如果你只需要你的代碼在**一個**地方進行編譯,並將其記錄爲UB有什麼問題。這說的很明顯,他應該能夠使用反向迭代器,但我只是回答**他的**問題 – aaronman

+3

@aaronman我很抱歉聽到你對downvote感到不滿。公平的說,我是三位大佬中唯一一位解釋我這樣做的原因。請不要私下接受,我沒有說你的答案是廢話,但是答案應該對未來的讀者有用。 UB真的是魔鬼,因爲它可以*無聲地破壞你的代碼。 – TemplateRex

+0

我個人會避免在我編寫的任何代碼中使用UB,但如果有人明確要求以正確的方式(使用反向迭代器)做正確的事情,並且我給他一個答案,提到答案是UB ID,看看有什麼大不了的。也尊重實際評論我的回答,所以我可以斥責你不像其他DV'rs – aaronman