简体   繁体   English

当我应该使用std :: map :: at来检索map元素

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

I have read different articles on web and questions at stackoverflow , but for me it is not clear is there any exclusive case when it is better to use std::map::at to retrieve map element. 我已经在stackoverflow上阅读了关于web和问题的不同文章,但是对于我来说,当使用std::map::at来检索map元素更好时,还不清楚是否存在任何独有的情况。

According to definition , std::map::at 根据定义std::map::at

Returns a reference to the mapped value of the element identified with key k. 返回对使用键k标识的元素的映射值的引用。

If k does not match the key of any element in the container, the function throws an out_of_range exception. 如果k与容器中任何元素的键不匹配,则该函数抛出out_of_range异常。

For me only case when it is worth to use std::map::at when you 100% sure that element with particular key exist, otherwise you should consider exception handling. 对我来说只有当你100%确定存在具有特定键的元素时才值得使用std::map::at ,否则你应该考虑异常处理。

  1. Is there any case where std::map::at considered as most efficient and elegant way to do? 是否有任何情况下std::map::at被认为是最有效和优雅的方式? In what cases you will recommend to use std::map::at ? 在什么情况下你会建议使用std::map::at
  2. Am I right that it is better to use map::find() when there is a possibility to not have element with such a key? 我是对的,当有可能没有带有这样一个键的元素时,最好使用map::find()吗? And map::find() it is faster and more elegant approach? map::find()它是更快更优雅的方法吗?
 if ( map.find("key") != map.end() ) { // found } else { // not found } 

ps PS

map::operator[] sometimes can be dangerous, because if an element doesn't exist then it will inserts it. map::operator[]有时可能很危险,因为如果一个元素不存在,那么它会插入它。

EDITED: links somehow related link 1 link 2 link 3 link 4 link 5 link 6 编辑:链接以某种方式相关链接1 链接2 链接3 链接4 链接5 链接6

Contrary to most existing answers here, note that there are actually 4 methods related to finding an element in a map (ignoring lower_bound , upper_bound and equal_range , which are less precise): 与此处的大多数现有答案相反,请注意实际上有4种与在地图中查找元素相关的方法(忽略lower_boundupper_boundequal_range ,这些方法不太精确):

  • operator[] only exist in non-const version, as noted it will create the element if it does not exist operator[]仅存在于非const版本中,如上所述,如果元素不存在,它将创建该元素
  • at() , introduced in C++11, returns a reference to the element if it exists and throws an exception otherwise at() ,在C ++ 11中引入,如果元素存在则返回对该元素的引用,否则抛出异常
  • find() returns an iterator to the element if it exists or an iterator to map::end() if it does not 如果元素存在, find()返回一个迭代器,如果不存在,则返回map::end()的迭代器
  • count() returns the number of such elements, in a map , this is 0 or 1 count()返回这些元素的数量,在map ,这是0或1

Now that the semantics are clear, let us review when to use which: 既然语义很清楚,那么让我们来看看何时使用哪个:

  • if you only wish to know whether an element is present in the map (or not), then use count() . 如果您只想知道map是否存在元素(或不存在),则使用count()
  • if you wish to access the element, and it shall be in the map , then use at() . 如果你想访问该元素,它应该在map ,然后使用at()
  • if you wish to access the element, and do not know whether it is in the map or not, then use find() ; 如果你想访问该元素,并且不知道它是否在map ,那么使用find() ; do not forget to check that the resulting iterator is not equal to the result of end() . 不要忘记检查生成的迭代器是否不等于end()的结果。
  • finally, if you wish to access the element if it exists or create it (and access it) if it does not, use operator[] ; 最后,如果您希望访问元素(如果存在)或创建它(并访问它),如果不存在,请使用operator[] ; if you do not wish to call the type default constructor to create it, then use either insert or emplace appropriately 如果您不希望调用类型默认构造函数来创建它,那么请适当地使用insertemplace

std::map::at() throws an out_of_range exception if the element could not be found. 如果找不到该元素, std::map::at()会抛出out_of_range异常。 This exception is a kind of logic_error exception which for me is a kind of synonym of assert() from the usage standpoint: it should be used to report errors in the internal logic of the program, like violation of logical preconditions or class invariants. 这个异常是一种logic_error异常,从使用的角度来看,对我来说这是一种assert()的同义词:它应该用于报告程序内部逻辑中的错误,比如违反逻辑前置条件或类不变量。

Also, you can use at() to access const maps. 此外,您可以使用at()来访问const映射。

So, for your questions: 所以,对于你的问题:

  1. I will recommend using at() instead of [] when accessing const maps and when element absence is a logic error. 我建议at()访问const映射时使用at()而不是[] ,当元素缺失是逻辑错误时。
  2. Yes, it's better to use map::find() when you're not sure element is here: in this case it's not a logic error and so throwing and catching std::logic_error exception will not be very elegant way of programming, even if we don't think about performance. 是的,当你不确定元素在这里时,最好使用map::find() :在这种情况下,它不是逻辑错误,因此抛出和捕获std::logic_error异常将不是非常优雅的编程方式,甚至如果我们不考虑表现。

As you noted, there are three different ways to access elements in a map: at() , operator[] and find() (there are also upper_bound , lower_bound and equal_range , but those are for more complicated circumstances where you might want to find a next/previous element etc.) 如您所述,有三种不同的方法可以访问地图中的元素: at()operator[]find() (还有upper_boundlower_boundequal_range ,但这些方法适用于您可能想要查找的更复杂的情况下一个/上一个元素等)

So, when should you use which one? 那么,你什么时候应该使用哪一个?

operator[] is basically "if it does not exist, create one with a default-constructed mapped element". operator[]基本上是“如果它不存在,则使用默认构造的映射元素创建一个”。 That means it won't throw (except in the corner cases when the memory allocation throws or one of the key or value constructors throw), and you definitely get a reference to the element you looked for - either the existing one or the newly created. 这意味着它不会抛出(除非在内存分配抛出或其中一个键或值构造函数抛出的角落情况下),并且您肯定会获得对您查找的元素的引用 - 现有元素或新创建的元素。

at() throws if there is no element for that key. 如果该键没有元素,则at()抛出。 Since you should not use exceptions for normal program flow, using at() is saying "I am sure there is such an element." 由于您不应该使用异常来处理正常的程序流,因此使用at()表示“我确信存在这样的元素”。 But with the added benefit that you get an exception (and not undefined behavior) if you are wrong. 但如果你错了,你会得到一个例外(而不是未定义的行为)的额外好处。 Don't use this if you are not positive that the element exists. 如果您不确定该元素是否存在,请不要使用此方法。

find() says "there may or may not be such an element, let's see..." and offers you the possibility to react to both cases differently. find()说“可能有也可能没有这样的元素,让我们看看......”并为您提供以不同方式对两种情况作出反应的可能性。 It therefore is the more general approach. 因此,它是更通用的方法。

All 3 of find , operator[] and at are useful. 所有3个findoperator[]at都很有用。

  • find is good if you don't want to accidentally insert elements, but merely act if they exist. 如果你不想意外地插入元素,那么find是好的,但只要它们存在就行动。

  • at is good if you expect that something should be on a map and you'd throw an exception if it wasn't anyway. at如果你期望的东西应该是一个地图上,你会抛出一个异常,如果不是反正是好的。 It can also access const maps in a more concise matter than find (where you can't use op[] ) 它还可以比find更简洁地访问const映射(在那里你不能使用op[]

  • op[] is good if you want to insert a default element, such as for the word counting program which puts an int 0 for every word encountered for the first time (with the idiom words[word]++; ). 如果你插入一个默认元素, op[]是好的,例如对于单词计数程序,它为第一次遇到的每个单词设置一个int 0 (用成语words[word]++; )。

This depends on what the requirements are for this function and how you are structuring the project. 这取决于此功能的要求以及您如何构建项目。 If you are supposed to return an object and you can't because it was not found then it leaves you with two options on how to handle that. 如果你应该返回一个对象但你不能因为它没有被找到,那么它将为你提供两个如何处理它的选项。 You could through an exception or you could return some sort of sentinel that means nothing was found. 你可以通过一个例外,或者你可以返回某种哨兵,这意味着没有找到任何东西。 If you want to throw an exception then use at() as the exception will be thrown for you. 如果要抛出异常,则使用at()因为异常将为您抛出。 If you do not want to throw an exception then use find() so you do not have to deal with handling an exception just to return a sentinel object. 如果您不想抛出异常,那么使用find()这样您就不必处理异常只是为了返回一个sentinel对象。

I think, it depends on your usecase. 我认为,这取决于你的用例。 The return type of std::map::at() is an lvalue reference to the value of the found element, while std::map::find() returns an iterator. std::map::at()的返回类型是对找到的元素的值的左值引用,而std::map::find()返回一个迭代器。 You might prefer 你可能更喜欢

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

in expressions over the more elaborate 在表达式上更精细

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

Whenever you use the result of std::map::at() in an expression, you expect the element to exist, and regard a missing element as an error. 每当在表达式中使用std::map::at()的结果时,您希望该元素存在,并将缺少的元素视为错误。 So an exception is a good choice to handle that. 所以异常是处理它的好选择。

I guess the difference is semantics. 我猜不同之处在于语义。

std::map::at() looks like this on my machine: 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;
}

As you can see, it uses lower_bound , then checks for end() , compares keys, and throws the exception where needed. 如您所见,它使用lower_bound ,然后检查end() ,比较密钥,并在需要时抛出异常。

find() looks like this: find()看起来像这样:

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

where _M_t is a red-black tree that stores the actual data. 其中_M_t是存储实际数据的红黑树。 Obviously, both function have the same (logarithmic) complexity. 显然,两个函数都具有相同的(对数)复杂度。 When you use find() + check for end() , you are doing almost the same thing that at does. 当你使用find() + check for end() ,你做的几乎和at一样。 I would say the semantic difference is: 我会说语义上的区别是:

  • use at() when you need an element at a specific location, and you assume that it is there. 当你需要一个特定位置的元素时,使用at() ,并假设它在那里。 In this case, the situation of the element missing from the desired place is exceptional, thus at() throws an exception. 在这种情况下,从所需位置丢失的元素的情况是例外的,因此at()抛出异常。
  • use find() when you need to find the element in the map. 需要在地图中查找元素时使用find() In this case the situation when the element is not present is normal. 在这种情况下,元素不存在的情况是正常的。 Also note that find() returns an iterator which you may use for purposes other than simply obtaining it's value. 另请注意, find()返回一个迭代器,您可以将其用于除了简单获取值之外的其他目的。

map::at() returns a l-value reference, and when you return by reference, you can use all its available benefits such as method chaining. map :: at()返回一个l值引用,当您通过引用返回时,可以使用其所有可用的好处,例如方法链。

example: 例:

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

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

operator[] also returns the mapped value by reference, but it may insert a value if searched for key is not found, in which case container size increases by one. operator[]也通过引用返回映射值,但是如果找不到找到的键,它可能会插入一个值,在这种情况下,容器大小会增加1。

This requires you to be extra cautious since you have to take care of iterator invalidation . 这需要您格外小心,因为您必须处理迭代器失效

Am I right that it is better to use map::find() when there is a possibility to not have element with such a key? 我是对的,当有可能没有带有这样一个键的元素时,最好使用map :: find()吗? And map::find() it is faster and more elegant approach? 而map :: find()它是更快更优雅的方法吗?

Yes, semantically it makes sense to use find() when you are not sure of the existence of element.Makes the code easier to understand even for a newbie. 是的,从语义上讲,当你不确定是否存在元素时使用find()是有意义的。即使对于新手来说,代码也更容易理解。

As for the time efficiency, map is generally implemented as a RB-tree/some balanced binary search tree and hence, complexity is O(logN) for find(). 至于时间效率,map通常实现为RB树/一些平衡二叉搜索树,因此,find()的复杂度为O(logN)。

C++ Spec: C ++规范:

T& operator[](const key_type& x); T&operator [](const key_type&x);
Effects: If there is no key equivalent to x in the map, inserts value_type(x, T()) into the map. 效果:如果地图中没有等效于x的键,则将value_type(x,T())插入到地图中。 Requires: key_type shall be CopyInsertable and mapped_type shall be DefaultInsertable into *this. 要求:key_type应为CopyInsertable,而mapped_type应为DefaultInsertable为* this。 Returns: A reference to the mapped_type corresponding to x in *this. 返回:对* this中x对应的mapped_type的引用。 4 Complexity: Logarithmic. 4复杂性:对数。

T& at(const key_type& x); T&at(const key_type&x);
const T& at(const key_type& x) const; const T&at(const key_type&x)const; Returns: A reference to the mapped_type corresponding to x in *this. 返回:对* this中x对应的mapped_type的引用。 Throws: An exception object of type out_of_range if no such element present. 抛出:如果没有这样的元素,则为out_of_range类型的异常对象。 Complexity: Logarithmic. 复杂性:对数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM