map with const keys but non const values?

后端 未结 5 611
半阙折子戏
半阙折子戏 2021-01-12 18:00

I have a situation, where I would like to have a map that does not allow to add/remove keys after initialization, but the values are allowed to change (thus I cannot simply

相关标签:
5条回答
  • 2021-01-12 18:44

    I usually regard this as a pitfall in C++ more than a feature, but, if it fits your application, you can just use pointer values.

    #include <map>
    #include <memory>
    
    int main(int argc, char ** argv)
    {
        using namespace std;
        const map<int, shared_ptr<int>> myMap = { {1, make_shared<int>(100)} };
        // *(myMap[1]) = 2;  // Does not compile
        *(myMap.at(1)) = 2;
        for (auto & element : myMap)
        {
            *(element.second) = 0;
        }
        return 0;
    }
    

    Which is really just a simpler version of this other answer (obviously you may choose between shared_ptr / unique_ptr as needed).

    0 讨论(0)
  • 2021-01-12 18:48

    Could you create a wrapper that contains the value that allows the value to be mutated when const and put that in the map instead? Something like:

    template<typename T>
    class Mutable {
        mutable T value;
    public:
      const Mutable& operator=(const T& v) const { value = v; return *this; }
      T& get() const { return value; }  
    };
    

    Then your map can be of type

    const std::map<int, Mutable<int>>
    

    Live demo.

    0 讨论(0)
  • 2021-01-12 18:48

    I understand that you simply want to disable the index access operator so that a user cannot accidentally add a default constructed item to the map. My solution is inspired by Chris Drew's solution but has the added benefit of remaining const correct (i.e. not allowing changing values of the map when the map is const).

    Essentially, by disabling default construction you remove the ability to invoke the index access operator provided by std::map. The other methods will remain available since std::map is a class template and member functions won't be evaluated until they are invoked. Hence, std::map::at will work fine but std::map::operator[] will result in a compile-time error.

    Inspired by Chris you can use a wrapper on the mapped_type to disable default construction. I took his demo and tweaked it a bit to demonstrate how to disable default construction and used it with std::map rather than a const std::map.

    template<typename T>
    class RemoveDefaultConstruction {
       T value;
    public:
       RemoveDefaultConstruction() = delete; // The magic is here
        RemoveDefaultConstruction(const RemoveDefaultConstruction &other) noexcept(std::is_nothrow_copy_constructible<T>::value) = default;
        RemoveDefaultConstruction(RemoveDefaultConstruction &&other) noexcept(std::is_nothrow_move_constructible<T>::value) = default;
        RemoveDefaultConstruction(T &&t) noexcept(std::is_nothrow_constructible<T, decltype(std::forward<T>(t))>::value) :
        value{std::forward<T>(t)} {
        }
    
        RemoveDefaultConstruction& operator=(const RemoveDefaultConstruction &other) = default;
        RemoveDefaultConstruction& operator=(RemoveDefaultConstruction &&other) = default;
        RemoveDefaultConstruction& operator=(T &&other) { value = std::move(other); return *this; }
        RemoveDefaultConstruction& operator=(T const &other) { value = other; return *this; }
    
        T const &get() const { return value; } // Keep const correctness
        T &get() { return value; } // Keep const correctness
    };
    
    void update(std::map<int, RemoveDefaultConstruction<int>> &m, int k, int v) { m.at(k) = v; }
    void update(std::map<int, RemoveDefaultConstruction<int>> const &m, int k, int v) {
       //m.at(k) = v; // ERROR: Cannot change a const value
    }
    

    Live Demo

    0 讨论(0)
  • 2021-01-12 19:03

    I see 2 options here

    1. Make the map const and use const_cast when changing something

      const std::map myMap;

      myMap[1] = 2; // NOT OK, because const map

      (const_cast&>(myMap)).at(1) = 2; // OK with const_cast

    2. make an wrapper class or derive an custom map that has only read and update existing value methods

    I don't think there is an built in way to make an map only with update value, and restrict and insert.

    0 讨论(0)
  • 2021-01-12 19:03

    Containers from the standard library are classes optimized for one usage that are expected to be used as is or included in higher level classes.

    Here your requirement (keys fixed after initialization) is not covered by the standart library containers, so you will have to build your own implementation. As it will not be a std::map, you can just implement the operations you need, probably nothing more that operator []...

    0 讨论(0)
提交回复
热议问题