Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a std::map of constant values which is still accessible by the [] operator?

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.

like image 600
Chaos_99 Avatar asked May 23 '13 08:05

Chaos_99


People also ask

How does map store values in C++?

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.

What is the complexity of std::map :: insert () method?

Time complexity: k*log(n) where n is size of map, k is no. of elements inserted.

Can a map have two keys with the same value C++?

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).

How do I change the values on a map in CPP?

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.


3 Answers

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.

like image 115
Matzar Avatar answered Sep 22 '22 20:09

Matzar


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.

like image 28
Some programmer dude Avatar answered Sep 23 '22 20:09

Some programmer dude


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.

like image 42
James Kanze Avatar answered Sep 24 '22 20:09

James Kanze