簡體   English   中英

當我應該使用std :: map :: at來檢索map元素

[英]When I should use std::map::at to retrieve map element

我已經在stackoverflow上閱讀了關於web和問題的不同文章,但是對於我來說,當使用std::map::at來檢索map元素更好時,還不清楚是否存在任何獨有的情況。

根據定義std::map::at

返回對使用鍵k標識的元素的映射值的引用。

如果k與容器中任何元素的鍵不匹配,則該函數拋出out_of_range異常。

對我來說只有當你100%確定存在具有特定鍵的元素時才值得使用std::map::at ,否則你應該考慮異常處理。

  1. 是否有任何情況下std::map::at被認為是最有效和優雅的方式? 在什么情況下你會建議使用std::map::at
  2. 我是對的,當有可能沒有帶有這樣一個鍵的元素時,最好使用map::find()嗎? map::find()它是更快更優雅的方法嗎?
 if ( map.find("key") != map.end() ) { // found } else { // not found } 

PS

map::operator[]有時可能很危險,因為如果一個元素不存在,那么它會插入它。

編輯:鏈接以某種方式相關鏈接1 鏈接2 鏈接3 鏈接4 鏈接5 鏈接6

與此處的大多數現有答案相反,請注意實際上有4種與在地圖中查找元素相關的方法(忽略lower_boundupper_boundequal_range ,這些方法不太精確):

  • operator[]僅存在於非const版本中,如上所述,如果元素不存在,它將創建該元素
  • at() ,在C ++ 11中引入,如果元素存在則返回對該元素的引用,否則拋出異常
  • 如果元素存在, find()返回一個迭代器,如果不存在,則返回map::end()的迭代器
  • count()返回這些元素的數量,在map ,這是0或1

既然語義很清楚,那么讓我們來看看何時使用哪個:

  • 如果您只想知道map是否存在元素(或不存在),則使用count()
  • 如果你想訪問該元素,它應該在map ,然后使用at()
  • 如果你想訪問該元素,並且不知道它是否在map ,那么使用find() ; 不要忘記檢查生成的迭代器是否不等於end()的結果。
  • 最后,如果您希望訪問元素(如果存在)或創建它(並訪問它),如果不存在,請使用operator[] ; 如果您不希望調用類型默認構造函數來創建它,那么請適當地使用insertemplace

如果找不到該元素, std::map::at()會拋出out_of_range異常。 這個異常是一種logic_error異常,從使用的角度來看,對我來說這是一種assert()的同義詞:它應該用於報告程序內部邏輯中的錯誤,比如違反邏輯前置條件或類不變量。

此外,您可以使用at()來訪問const映射。

所以,對於你的問題:

  1. 我建議at()訪問const映射時使用at()而不是[] ,當元素缺失是邏輯錯誤時。
  2. 是的,當你不確定元素在這里時,最好使用map::find() :在這種情況下,它不是邏輯錯誤,因此拋出和捕獲std::logic_error異常將不是非常優雅的編程方式,甚至如果我們不考慮表現。

如您所述,有三種不同的方法可以訪問地圖中的元素: at()operator[]find() (還有upper_boundlower_boundequal_range ,但這些方法適用於您可能想要查找的更復雜的情況下一個/上一個元素等)

那么,你什么時候應該使用哪一個?

operator[]基本上是“如果它不存在,則使用默認構造的映射元素創建一個”。 這意味着它不會拋出(除非在內存分配拋出或其中一個鍵或值構造函數拋出的角落情況下),並且您肯定會獲得對您查找的元素的引用 - 現有元素或新創建的元素。

如果該鍵沒有元素,則at()拋出。 由於您不應該使用異常來處理正常的程序流,因此使用at()表示“我確信存在這樣的元素”。 但如果你錯了,你會得到一個例外(而不是未定義的行為)的額外好處。 如果您不確定該元素是否存在,請不要使用此方法。

find()說“可能有也可能沒有這樣的元素,讓我們看看......”並為您提供以不同方式對兩種情況作出反應的可能性。 因此,它是更通用的方法。

所有3個findoperator[]at都很有用。

  • 如果你不想意外地插入元素,那么find是好的,但只要它們存在就行動。

  • at如果你期望的東西應該是一個地圖上,你會拋出一個異常,如果不是反正是好的。 它還可以比find更簡潔地訪問const映射(在那里你不能使用op[]

  • 如果你插入一個默認元素, op[]是好的,例如對於單詞計數程序,它為第一次遇到的每個單詞設置一個int 0 (用成語words[word]++; )。

這取決於此功能的要求以及您如何構建項目。 如果你應該返回一個對象但你不能因為它沒有被找到,那么它將為你提供兩個如何處理它的選項。 你可以通過一個例外,或者你可以返回某種哨兵,這意味着沒有找到任何東西。 如果要拋出異常,則使用at()因為異常將為您拋出。 如果您不想拋出異常,那么使用find()這樣您就不必處理異常只是為了返回一個sentinel對象。

我認為,這取決於你的用例。 std::map::at()的返回類型是對找到的元素的值的左值引用,而std::map::find()返回一個迭代器。 你可能更喜歡

return myMap.at("asdf"s) + 42;

在表達式上更精細

return myMap.find("asdf"s)->second + 42;

每當在表達式中使用std::map::at()的結果時,您希望該元素存在,並將缺少的元素視為錯誤。 所以異常是處理它的好選擇。

我猜不同之處在於語義。

std::map::at()在我的機器上看起來像這樣:

mapped_type&
at(const key_type& __k)
{
    iterator __i = lower_bound(__k);
    if (__i == end() || key_comp()(__k, (*__i).first))
        __throw_out_of_range(__N("map::at"));
    return (*__i).second;
}

如您所見,它使用lower_bound ,然后檢查end() ,比較密鑰,並在需要時拋出異常。

find()看起來像這樣:

iterator
find(const key_type& __x)
{ return _M_t.find(__x); }

其中_M_t是存儲實際數據的紅黑樹。 顯然,兩個函數都具有相同的(對數)復雜度。 當你使用find() + check for end() ,你做的幾乎和at一樣。 我會說語義上的區別是:

  • 當你需要一個特定位置的元素時,使用at() ,並假設它在那里。 在這種情況下,從所需位置丟失的元素的情況是例外的,因此at()拋出異常。
  • 需要在地圖中查找元素時使用find() 在這種情況下,元素不存在的情況是正常的。 另請注意, find()返回一個迭代器,您可以將其用於除了簡單獲取值之外的其他目的。

map :: at()返回一個l值引用,當您通過引用返回時,可以使用其所有可用的好處,例如方法鏈。

例:

  map<int,typ> table;
  table[98]=a;
  table[99]=b;

  table.at(98)=table.at(99);

operator[]也通過引用返回映射值,但是如果找不到找到的鍵,它可能會插入一個值,在這種情況下,容器大小會增加1。

這需要您格外小心,因為您必須處理迭代器失效

我是對的,當有可能沒有帶有這樣一個鍵的元素時,最好使用map :: find()嗎? 而map :: find()它是更快更優雅的方法嗎?

是的,從語義上講,當你不確定是否存在元素時使用find()是有意義的。即使對於新手來說,代碼也更容易理解。

至於時間效率,map通常實現為RB樹/一些平衡二叉搜索樹,因此,find()的復雜度為O(logN)。

C ++規范:

T&operator [](const key_type&x);
效果:如果地圖中沒有等效於x的鍵,則將value_type(x,T())插入到地圖中。 要求:key_type應為CopyInsertable,而mapped_type應為DefaultInsertable為* this。 返回:對* this中x對應的mapped_type的引用。 4復雜性:對數。

T&at(const key_type&x);
const T&at(const key_type&x)const; 返回:對* this中x對應的mapped_type的引用。 拋出:如果沒有這樣的元素,則為out_of_range類型的異常對象。 復雜性:對數。

暫無
暫無

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

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