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.
According to definition, std::map::at
Returns a reference to the mapped value of the element identified with key k.
If k does not match the key of any element in the container, the function throws an out_of_range exception.
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.
- Is there any case where
std::map::at
considered as most efficient and elegant way to do? In what cases you will recommend to usestd::map::at
? - Am I right that it is better to use
map::find()
when there is a possibility to not have element with such a key? Andmap::find()
it is faster and more elegant approach?
if ( map.find("key") != map.end() ) { // found } else { // not found }
p.s
map::operator[]
sometimes can be dangerous, because if an element doesn't exist then it will inserts it.
EDITED: links somehow related link 1 link 2 link 3 link 4 link 5 link 6
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, whilestd::map::find()
returns an iterator. You might preferin expressions over the more elaborate
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. 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:As you can see, it uses
lower_bound
, then checks forend()
, compares keys, and throws the exception where needed.find()
looks like this:where
_M_t
is a red-black tree that stores the actual data. Obviously, both function have the same (logarithmic) complexity. When you usefind()
+ check forend()
, you are doing almost the same thing thatat
does. I would say the semantic difference is:at()
when you need an element at a specific location, and you assume that it is there. In this case, the situation of the element missing from the desired place is exceptional, thusat()
throws an exception.find()
when you need to find the element in the map. In this case the situation when the element is not present is normal. Also note thatfind()
returns an iterator which you may use for purposes other than simply obtaining it's value.As you noted, there are three different ways to access elements in a map:
at()
,operator[]
andfind()
(there are alsoupper_bound
,lower_bound
andequal_range
, but those are for more complicated circumstances where you might want to find a next/previous element etc.)So, when should you use which one?
operator[]
is basically "if it does not exist, create one with a default-constructed mapped element". 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. Since you should not use exceptions for normal program flow, usingat()
is saying "I am sure there is such an element." 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. It therefore is the more general approach.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
andequal_range
, which are less precise):operator[]
only exist in non-const version, as noted it will create the element if it does not existat()
, introduced in C++11, returns a reference to the element if it exists and throws an exception otherwisefind()
returns an iterator to the element if it exists or an iterator tomap::end()
if it does notcount()
returns the number of such elements, in amap
, this is 0 or 1Now that the semantics are clear, let us review when to use which:
map
(or not), then usecount()
.map
, then useat()
.map
or not, then usefind()
; do not forget to check that the resulting iterator is not equal to the result ofend()
.operator[]
; if you do not wish to call the type default constructor to create it, then use eitherinsert
oremplace
appropriatelystd::map::at()
throws anout_of_range
exception if the element could not be found. This exception is a kind oflogic_error
exception which for me is a kind of synonym ofassert()
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.Also, you can use
at()
to access const maps.So, for your questions:
at()
instead of[]
when accessing const maps and when element absence is a logic error.map::find()
when you're not sure element is here: in this case it's not a logic error and so throwing and catchingstd::logic_error
exception will not be very elegant way of programming, even if we don't think about performance.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. If you do not want to throw an exception then usefind()
so you do not have to deal with handling an exception just to return a sentinel object.