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

你说的曾经没有我的故事 提交于 2019-12-01 17:27:09

问题


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.


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


If the map can't mutate, you should use const map<string, int>, and not map<string, const int>: the second version allows insertion and deletion of objects.

Sadly, you are going to have to lose the [] operator; C++ doesn't have an ImmutableMap, or something like that. However, std::map::at and std::map::find aren't too bad...




回答5:


Something much simpler which has a smaller footprint and is faster:

static const int MyMapData[] = {
    42      // index 0 is mapped to "key"
};

struct MyMap { const int& operator[](std::string key) const { switch (key) { case "Keys": return MyMapData[0];

default: return NotANumber; // Return 0 and raise an assertion, or return "Not a Number". } }

};

Easy to maintain, no use of templates, no use of boost libraries and compilable everywhere.



来源:https://stackoverflow.com/questions/16708618/how-to-create-a-stdmap-of-constant-values-which-is-still-accessible-by-the

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!