What is the difference in getting a value through aMap[key]
and aMap.at(key)
in C++?
In C++11 map::at
exists (who knew?).
It throws an exception if the key doesn't exist, find
returns aMap.end()
if the element doesn't exist, and operator[]
value-initializes a new value for the corresponding key if no value exists there.
If you access a key using the indexing operator []
that is not currently a part of a map, then it automatically adds a key for you. This is a huge caveat, and take this into consideration. For this reason, I prefer using the indexing operator []
for setting, and .find()
/ .at()
for lookup.
Another advantage of using .at()
over []
is the fact that it can operate on a const std::map
, whereas []
won't.
In C++11 map::at
exists (who knew?).
It throws an exception if the key doesn't exist, find
returns aMap.end()
if the element doesn't exist, and operator[]
value-initializes a new value for the corresponding key if no value exists there.
I like the answers here, but I think it's important to know WHY [] can't be used on a const
instance of std::map
. If we look at the standard declaration of these methods:
mapped_type& at(const key_type& k);
const mapped_type& at(const key_type& k) const;
mapped_type& operator[](const key_type& k);
mapped_type& operator[](key_type&& k);
As we can see, all of the operator[]
are non const
and thus say that the mapped_type
returned will be modifiable. On the other hand, a const& at() const
version exists, which doesn't allow the modification of the mapped_type, nor the class itself.
Now let's take a look why that is in the implementation of the operator[]
:
template <class _Key, class _Tp, class _Compare, class _Allocator>
_Tp&
map<_Key, _Tp, _Compare, _Allocator>::operator[](const key_type& __k)
{
return __tree_.__emplace_unique_key_args(__k,
_VSTD::piecewise_construct,
_VSTD::forward_as_tuple(__k),
_VSTD::forward_as_tuple()).first->__get_value().second;
}
Here we can see, that this method tries to directly emplace the key into the binary tree as a new unique key pair, where the emplace method then returns a reference to the std::pair
(no matter if it already existed or not). This tree is a member of std::map
, and thus returning a reference to it's data can potentially modify the data AND with that the tree itself, and thus the std::map
class so that why it's impossible to call this method on a const
instance of std::map
.
Now, let's take a look at the implementation of at() const
:
template <class _Key, class _Tp, class _Compare, class _Allocator>
const _Tp&
map<_Key, _Tp, _Compare, _Allocator>::at(const key_type& __k) const
{
__parent_pointer __parent;
__node_base_pointer __child = __tree_.__find_equal(__parent, __k);
if (__child == nullptr)
__throw_out_of_range("map::at: key not found");
return static_cast<__node_pointer>(__child)->__value_.__get_value().second;
}
This method accesses the tree in a completely different way. Because it only searches for an equal, which is already emplaced in the tree. And operates with a constant pointer to the found node. The non-const
version of the at()
method operates very similarly, but it requests the __node_base_pointer
to be a reference, thus making it possible to modify it, but still eliminates the possibility to emplace new nodes with the at()
method.
© 2022 - 2024 — McMap. All rights reserved.