Search code examples
c++setmutable

How can I improve this design that forces me to declare a member function const and declare variables mutable?


For some reason I am iterating over elements of a class in an std::set and would like to slightly modify the keys, knowing that the order will be unchanged.

Iterators on std::set are const_iterators because if the key is modified, it might result in a bad order and therefore in set corruption. However I know for sure that my operations won't change the order of my elements in the set.

For the moment, here is my solution:

class Foo
{
public:
    Foo(int a, int b): a_(a),b_(b) {}
   ~Foo(){}
    bool operator < (const Foo& o) const { return this.a_ < o.a_ ; }
    void incrementB() const { ++b_; } // <-- the problem: it is not const!
private:
    const int a_;
    mutable int b_;                   // <-- I would like to avoid this
}

void f()
{
    std::set<Foo> s;
    // loop and insert many (distinct on a_) Foo elements;
    std::for_each(s.begin(), c.end(), [](const Foo& s) { s.incrementB(); }); // Foo must be const. iterators are const_iterators
}

How would you modify it (I know I could use an std::map but I am curious whether you can suggest other options) to remove mutable and const?

Thanks


Solution

  • You can't. Set elements are required to be const for container correctness:

    It forces you to realize that the key part needs to be immutable, or the data structure invariants would be broken.

    struct element 
    {
         std::string key_part; // const in the set
    
         bool operator<(const element&o) const { return key_part<o.key_part; }
    
      private:
         mutable int m_cached; // non-key, *NOT* used in operator<
    };
    

    If you wanted to retain the possibility to 'express' const-ness in the non-key part, split it out into pairs and store them in a map:

    std::map<std::string /*key_part*/, int /*m_cached*/> mapped;
    

    or, more flexibly:

    struct element 
    {
         std::string key_part; // const in the set
    
         bool operator<(const element&o) const { return key_part<o.key_part; }
    
         struct value {
             int m_cached;
             int m_moredata; //...
         } /*not in the element itself*/;
    };
    
    std::map<element, element::value> mapped;