Why Does Std::Map Not Have a Const Accessor

Why does std::map not have a const accessor?

operator[] in a map returns the value at the specified key or creates a new value-initialized element for that key if it's not already present, so it would be impossible.

If operator[] would have a const overload, adding the element wouldn't work.

That answers the question. Alternatives:

For C++03 - you can use iterators (these are const and non-const coupled with find). In C++11 you can use the at method.

Is the at() const accessor for map standard in C++11?

Yes. std::map has an at member function in C++11 with the following specification (23.4.4.3/9):

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 is present.

Complexity: logarithmic.

Note however that this member function has been added specifically to std::map. It is not required by the more general associative container requirement. If you are writing generic code that requires some associative container type, you can't use this new at. Instead, you should continue to use find, which is part of the associative container concept, or write your own non-member helper:

template <typename AssociativeContainer>
typename AssociativeContainer::mapped_type&
get_mapped_value(AssociativeContainer& container,
typename AssociativeContainer::key_type const& key)
{
typename AssociativeContainer::iterator it(container.find(key));
return it != container.end() ? it->second : throw std::out_of_range("key");
}

template <typename AssociativeContainer>
typename AssociativeContainer::mapped_type const&
get_mapped_value(AssociativeContainer const& container,
typename AssociativeContainer::key_type const& key)
{
typename AssociativeContainer::const_iterator it(container.find(key));
return it != container.end() ? it->second : throw std::out_of_range("key");
}

Or, if you have an implementation that supports rvalue references and decltype, you don't need two overloads:

template <typename AssociativeContainer, typename Key>
auto get_mapped_value(AssociativeContainer&& container, Key const& key)
-> decltype(std::declval<AssociativeContainer>().begin()->second)&
{
auto const it(container.find(key));
return it != container.end() ? it->second : throw std::out_of_range("key");
}

(Or something close to that; one fun thing about C++11 is that no two compilers have the same bugs and all seem to accept slightly different subsets of valid--and invalid--C++11 code.)

Why does the following not work when the map is const static

Using operator[] on a std::map creates the object if it doesn't exist. So it's an operation that can only be performed on a map you are allowed to modify. Use find instead.

C++ map access discards qualifiers (const)

std::map's operator [] is not declared as const, and cannot be due to its behavior:

T& operator[] (const Key& key)

Returns a reference to the value that is mapped to a key equivalent to key, performing insertion if such key does not already exist.

As a result, your function cannot be declared const, and use the map's operator[].

std::map's find() function allows you to look up a key without modifying the map.

find() returns an iterator, or const_iterator to an std::pair containing both the key (.first) and the value (.second).

In C++11, you could also use at() for std::map. If element doesn't exist the function throws a std::out_of_range exception, in contrast to operator [].

Compiler error when attempting to access std::map element with operator[]

It seems that m_components is of type ComponentMap*.
When you write m_components["Armor"] compiler interprets that as an access to "Armor"-th element of dynamic array of ComponentMaps, which does not make any sense.

What you want is (*m_components)["some string"]. This will invoke operator[] of ComponentMap, but as Luchian Grigore and Olaf Dietsche mention, std::map::operator[] does not have a const overload, so this will fail too. The only option left is to use find.

The simplified edition will be:

Armor* Character::getArmor() const
{
return static_cast<Armor*>(m_components->find("Armor")->second);
}

Weapon* Character::getWeapon() const
{
return static_cast<Weapon*>(m_components->find("Weapon")->second);
}

Attributes* Character::getAttributes() const
{
return static_cast<Attributes*>(m_components->find("Attributes")->second);
}

This code does not have the same behaviour as your original example and will fail if m_components does not have "Armor", "Weapon" and "Attributes" elements.
The closest we can get is to explicitly handle absence of element and return 0 or nullptr if you use C++11.

Final correct C++03 compatible edition:

Armor* Character::getArmor() const
{
ComponentMapCIter i = m_components->find("Armor");
if (i != m_components->end())
return static_cast<Armor*>(i->second);
return 0;
}

Weapon* Character::getWeapon() const
{
ComponentMapCIter i = m_components->find("Weapon");
if (i != m_components->end())
return static_cast<Weapon*>(i->second);
return 0;
}

Attributes* Character::getAttributes() const
{
ComponentMapCIter i = m_components->find("Attributes");
if (i != m_components->end())
return static_cast<Attributes*>(i->second);
return 0;
}

Why is a default constructor required when storing in a map?

mymap["hello"] can attempt to create a value-initialized A, so a default constructor is required.

If you're using a type T as a map value (and plan to access value via operator[]), it needs to be default-constructible - i.e. you need a parameter-less (default) constructor. operator[] on a map will value-initialize the mapped value if a value with the key provided is not found.

C++ error: passing 'const std::mapint, std::basic_stringchar ' as 'this' argument of ...

string color::getColorText() const {
return colors[cColortype];
}

The issue is that you've marked the function as const. The operator[] on std::map is marked as non-const, and cannot be used in a const function like this. You need to manually use std::map::find (or other mechanism) to search for the input type and handle the case where it's not found.

If you're using C++11, you can instead use std::map::at, which IS allowed to be used on a constant map, and throws an exception if the requested element is not present.

Does std::map require the comparator's operator() to be const?

Well, yes: The comparator is a subobject of the map itself, one way or another (maybe a member; usually a base class of some inner implementation class). If you have a constant reference to the map, the comparator still needs to be usable for lookup, so the operator needs to be const.

Why can't I have two accessors for the same element in tbb hash map?

An accessor allows write access. This means a write lock is acquired, and held by no more than a single accessor. You enter a deadlock because the same thread attempts to lock the same element for writing via different accessors.

If all you want is to read the data, then use a const_accessor with find. It will acquire a read lock only. Multiple read locks can be acquired and held without deadlocking.

concurrent_hash_map<int, int>::const_accessor a1, a2; 


Related Topics



Leave a reply



Submit