A map with constant keys but not constant values?

I have a situation where I would like to have a map that does not allow adding / removing keys after initialization, but the values ​​can change (therefore, I can’t just make a const map). Those.

 /*semi-const*/ map<int,int> myMap = initMap(); myMap[1] = 2; // NOT OK, because potentially adds a new key myMap.at(1) = 2; // OK, because works only if key is present for (auto & element : myMap) { element.second = 0; // OK, values may change } 

I could write my own shell for std::map , but I have a feeling that this is something not too unusual, so I wonder if there is already an existing solution.

Is there a standard idiom for a card that does not allow adding / removing keys, and the values ​​can change?

ps: I know that only one name is a bit vague because the keys are already const on the map, but I hope this is clear, what I mean ...

+5
source share
5 answers

Failed to create a wrapper containing a value that allows you to change the value with const and instead place it in map ? Sort of:

 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 card may be of type

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

Live demo .

+4
source

Containers from the standard library are single-use optimized classes that are expected to be used as or included in higher-level classes.

Here, your requirement (keys fixed after initialization) is not covered by standard library containers, so you will need to create your own implementation. Since there will be no std::map , you can simply perform the necessary operations, perhaps no more than operator [] ...

+1
source

I usually see this as a trap in C ++ more than a function, but if it suits your application, you can simply 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; } 

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

+1
source

I understand that you just want to disable the index access operator so that the user cannot accidentally add the default element to the map. My solution is inspired by the decision of Chris Drew, but it has an additional advantage over the rest of the constant (i.e. it does not allow changing the values ​​of the map when the map is a constant).

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

Inspired by Chris, you can use the shell on mapped_type to disable the default build. I took his demo and modified it a bit to demonstrate how to disable the default build and use it with std::map , not 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

+1
source

I see 2 options here

  • Make a const map and use const_cast when changing anything

    const std :: map myMap;

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

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

  • create a wrapper class or get a custom map that only reads and updates existing value methods

I do not think that there is a built-in way to make a card with only an updated value, as well as limit and insert.

0
source

All Articles