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

本秂侑毒 提交于 2019-12-01 17:37:59

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

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.

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