Difference between map[] and map.at in C++?
Asked Answered
M

3

30

What is the difference in getting a value through aMap[key] and aMap.at(key) in C++?

Mystic answered 30/5, 2012 at 17:38 Comment(0)
D
26

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.

Dorpat answered 30/5, 2012 at 17:40 Comment(1)
@KerrekSB (since C++11) didn't know that though.Dorpat
C
47

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.

Christner answered 30/5, 2012 at 17:41 Comment(0)
D
26

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.

Dorpat answered 30/5, 2012 at 17:40 Comment(1)
@KerrekSB (since C++11) didn't know that though.Dorpat
C
0

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.

Cirilo answered 21/6 at 11:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.