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 use std::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? And map::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
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):
operator[] only exist in non-const version, as noted it will create the element if it does not exist
at(), introduced in C++11, returns a reference to the element if it exists and throws an exception otherwise
find() returns an iterator to the element if it exists or an iterator to map::end() if it does not
count() returns the number of such elements, in a map, this is 0 or 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().
if you wish to access the element, and it shall be in the map, then use at().
if you wish to access the element, and do not know whether it is in the map or not, then use find(); do not forget to check that the resulting iterator is not equal to the result of end().
finally, if you wish to access the element if it exists or create it (and access it) if it does not, use operator[]; if you do not wish to call the type default constructor to create it, then use either insert or emplace appropriately
std::map::at() throws an out_of_range exception if the element could not be found. 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.
Also, you can use at() to access const maps.
So, for your questions:
I will recommend using at() instead of [] when accessing const maps and when element absence is a logic error.
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.
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.)
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, using at() 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.
All 3 of find, operator[] and at are useful.
find is good if you don't want to accidentally insert elements, but merely act if they exist.
at is good if you expect that something should be on a map and you'd throw an exception if it wasn't anyway. It can also access const maps in a more concise matter than find (where you can't use 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]++;).
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 use find() so you do not have to deal with handling an exception just to return a sentinel object.
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. 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. 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:
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.
find() looks like this:
iterator
find(const key_type& __x)
{ return _M_t.find(__x); }
where _M_t is a red-black tree that stores the actual data. 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. 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. In this case, the situation of the element missing from the desired place is exceptional, thus at() throws an exception.
use 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 that find() returns an iterator which you may use for purposes other than simply obtaining it's value.
map::at() returns a l-value reference, and when you return by reference, you can use all its available benefits such as method chaining.
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.
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? And map::find() it is
faster and more elegant approach?
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.
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().
C++ Spec:
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.
Requires: key_type shall be CopyInsertable and mapped_type shall be
DefaultInsertable into *this. Returns: A reference to the
mapped_type corresponding to x in *this. 4 Complexity: Logarithmic.
T& at(const key_type& x);
const T& at(const key_type& x) const;
Returns: A reference to the mapped_type corresponding to x in *this.
Throws: An exception object of type out_of_range if no such element present.
Complexity: Logarithmic.
Related
I understand why an unordered_map has an iterator (begin() and end()) but I don't see what value returning an iterator from find / emplace is.
I can't imagine a use case where you would want to start your iterator at a given key as opposed to begin / end (because a good hash function should make the position of a key in the table arbitrary).
The alternative; to just return the value, reference to the value, or pair of key and value covers all use cases I can imagine an avoids the unnecessary work of creation an iterator and likely causing an additional memory deference when using it to access the underlying pair / value.
What if you want to remove the item? What if you want to extract or merge it? What if you want to use it as a hint to emplace?
std::unordered_set::erase() has 3 overloads: In the one taking a reference, passing an "invalid" value, i.e. one that doesn't exist in the set, simply makes erase() return 0. But what about the other two overloads?
Does the C++11 standard say what erase() should do in this case, or it's compiler dependent? Is it supposed to return end() or undefined behavior?
I couldn't find an answer in the specification, cppreference.com, cplusplus.com. On IBM site they say it returns end() if no element remains after the operation, but what happens if the operation itself fails due to an invalid iterator?
And in general, do erase() methods for STL containers simply have undefined behavior in these case?
(so I need to check my iterators before I pass any to erase(), or use the unordered_set::erase() overload which takes a value_type reference, which would simply return 0 if it fails)
There is a big semantic difference between trying to remove a value that doesn't occur in set and trying to erase from a invalid iterator.
Trying to use an invalid iterator is undefined behaviour and will end badly.
Do you have a specific use-case you are thinking of when you might want to erase an invalid iterator?
These are two completely different cases. There is no "invalid value", values that don't exist in the set are still valid. So you pass a valid value that s not contained in the set and thus get 0 returned - no elements have been erased.
The other overloads are completely different. The standard requires the iterators passed to the erase methods to be "valid and dereferencable" and "a valid iterator range", respectively. Otherwise the behavior is undefined.
So yes, iterators have to be valid. But you cannot check if an iterator is valid programmatically - you have to make sure from your program logic, that they are.
I realize I can get the iterator reference by calling back() but why not return it with push_back() as well? Is it for performance reasons? Or is it due to exception safety (similar to why pop_back() doesn't return the popped value)? In either case, please explain.
Various insert functions return an iterator for a very simple reason: the caller does not necessarily know how to get an iterator to that element. map::insert and set::insert return one because otherwise, the caller would have to search for the element.
When you do a vector::push_back, you know where the inserted element is. It's --vector.end(). You don't have to search for it; it's always that location. You can get it in constant time, and pretty quick constant time at that.
So there's really no point in returning something that the user already knows.
I don't know if there is an official reason but the most immediate that comes to my mind is performance. If you look at the majority of uses of push_back and push_front the return value is not used. Where an element is pushed is not so important as that it's pushed.
Why return a non-free value that is almost never used?
The reason is that vector provides an insert method that does return an iterator to the inserted position. The standard is consistent in that push_back doesn't return a value but some forms of insert will, when available and appropriate.
I am using std::multimap<> and I pass pointer to an element (T*) to a component written in C.
When the component wants to delete the object it calls back to C++ supplying the pointer, however, I am not sure whether there is a way to convert T* into std::multimap<>::iterator so that I can call erase().
Any ideas?
If you can determine the key from the item, you can use equal_range to get all the possible matches, then call find on that range.
If there isn't a way to get from an item to it's key (rare but possible), then one could enumerate through the whole multimap (from begin() to end()) and erase the one that matches. Hopefully this would be a rare operation, as it is O(N).
Do not confuse pointers and iterators. Sometimes (e.g. arrays) a pointer can function as iterator. But it doesn't necessarily.
Iterators in C++ will usually overload the * operator aka "dereference operator". This makes them look like C pointers even more, when they technically may or may not be the same.
Passing iterators is in general fragile, and I'd avoid this. In particular, a concurrent modification of the multimap in your case may render the iterator invalid.
Remember that a multimap is a set of key-value pairs. It sounds like your T* is a value and you need an efficient way to get its key so you can remove it. Have you considered Boost.Bimap? That library allows efficient mappings both ways. Then it should be simple to take the T* from the calling code, lookup the key, and erase it.
Is it a feature of unordered_map::find() to insert the key that we lookup with 0 value automatically?
Let me make this clear
unordered_map<int, int> tempMap;
//some code
if(tempMap.find(1) == tempMap.end()){
//do something but not insert the 1 with a corresponding value into the tempMap
}
so if I lookup 1 again, will it be there in the tempMap having 0 as corresponding value.
Is that a feature of unordered_map?
No, find only searches and returns an iterator.
That said, std::unordered_map (and std::map) both overload operator[] which will insert a default if needed:
// if element with a key value one exists, return that. otherwise, insert
// a default initialized element (zero in this case), and return that
auto& val = tempMap[1];
No -- find does not insert values.
If you want to insert the value if it was not previously present, then you can use operator[] instead of find.
This is done because operator[] returns a reference to an object. Since there's no such thing as a null reference, essentially the only alternative would be to have it throw an exception when searching for an item that wasn't previously present. There have been some containers that adopted that behavior, but it wasn't apparently very useful and never gained much popularity (I used some containers like that and found it a pain).