I need a std:map data structure that is read only, which means I have to fill it once with data and then only read those values, never change them or add additional ones.
My non-const version looks like this:
//in .h
#include <string>
#include <map>
std::map<std::string, int> myMap;
void initMap();
//in .cpp
#include "foo.h"
void initMap() {
myMap["Keys"] = 42;
}
Then I'd call initMap()
once in my code and be done.
Now I've read several questions here already and it seems non-trivial to achieve const-ness for the map.
Making it a std::map<std::string, const int>
won't allow me to fill it in the initMap()
.
Filling it with a non-const temp and the copy constructor on definition doesn't work either, as the copy constructor doesn't easily take the non-const version as input.
Making it a const std::map<std::string, int>
(which I could fill with a non-const copy during definition) would disable the use of the []
operator for value access.
So is there a way to achieve (value) const-ness and initialize the structure (preferably in the header file)?
BTW: Neither C++0x nor C++11 nor boost::
is an option.
Maps are associative containers that store elements in a combination of key values and mapped values that follow a specific order. No two mapped values can have the same key values. In C++, maps store the key values in ascending order by default. A visual representation of a C++ map.
Time complexity: k*log(n) where n is size of map, k is no. of elements inserted.
This means that for every unique key, there is a data value mapped to it, that can be easily accessed if we know the key. This is why every key has to be unique, and no two keys can be the same(but the values associated to keys can be the same).
To update an existing value in the map, first we will find the value with the given key using map::find() function. If the key exists, then will update it with new value.
Couldn't you use the insert()
method available for std::map
?
http://www.cplusplus.com/reference/map/map/insert/
Edit : (solution) myMap.insert(std::pair<std::string, const int>("Keys", 42));
As far as I understand it, the reason why this works is because the constructor for the pair, pair (const first_type& a, const second_type& b)
, initializes its members first
and second
using the constructors for first_type
and second_type
, taking a
and b
as their respective parameter.
With the solution you were trying to use, my comprehension is that myMap["Keys"] = 42;
initializes the member second
of the map
(of type const int
) using the default constructor for int
. Then a value is attempted to be assigned to that member. As this is done outside the constructor of the class map
, the const
declaration makes this impossible.
With the solution using insert()
, the members are initialized in the constructor of the pair
. Thus they can be declared const
. The same operation is done when the pair
is copied to the map
.
While this is not possible for you, others who wants to do this and that have a C++11 compatible compiler, could use uniform initialization:
std::map<std::string, const int> myMap = {
{ "keys", 42 }
};
Oh and by the way, don't define the map in the header file. Instead declare it as extern
in the header file, then define it in the source file.
The simplest solution is to write your own, wrapping the standard map class:
template <typename KeyType, typename MappedType, typename CmpType>
class ConstantMap
{
typedef std::map<KeyType, MappedType, CmpType> Impl;
Impl myImpl;
public:
typedef Impl::value_type value_type;
template <ForwardIterator>
ConstantMap( ForwardIterator begin, ForwardIterator end, CmpType cmp = CmpType() )
: myImpl( begin, end, cmp )
{
}
// necessary if [] is not going to work for missing keys
bool contains( KeyType const& key ) const
{
return myImpl.find( key ) != myImpl.end();
}
MappedType const& operator[]( KeyType const& key ) const
{
Impl::const_iterator elem = myImpl.find( key );
if ( elem == myImpl.end() ) {
// Not found, do what you want (maybe throw an exception)
}
return elem.second;
}
};
You can initialze the map by passing it iterators to a sequence
of anything which can convert to value_type
.
Depending on your needs, you may want to add additional forwarding typedefs, functions, etc. If you're using C++11, you may also want to create a constructor which can use a list initializer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With