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

前端 未结 3 1132
情深已故
情深已故 2020-12-20 20:26

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.

相关标签:
3条回答
  • 2020-12-20 20:42

    Another option is to const_cast to a reference type :

    class Foo
    {
    public:
        void incrementB() const { ++ const_cast< int& >( b_ ); }
    private:
        int b_;
    };
    

    But as sehe already said, you shouldn't modify set's elements.

    0 讨论(0)
  • 2020-12-20 20:49

    One possibility might be to factor out the value part of Foo in a pimpl.

    class Element
    {
    public:
    
        Element(int key, int value);
    
        Element( const Element& el );
        Element( Element&& el );
    
        ~Element();
    
        bool operator < (const Element& o) const;
    
        void incrementValue() const;
        int  getValue() const;
    
    private:
    
        Element& operator=(const Element& );
        Element& operator=( Element&& el );
    
        struct Key
        {
            Key( const int key ) : m_KeyValue( key )
            {
            };
    
            const int m_KeyValue;
        };
    
        struct Value;
    
        const   Key                 m_Key;
        std::unique_ptr<Value>      m_Value;
    
    };
    
    struct Element::Value
    {
        Value( int val ) : value(val)
        {
    
        }
    
        int value;
    };
    
    Element::Element(int key, int value) : 
        m_Key(key),
        m_Value( new Element::Value(value) )
    {
    
    }
    
    Element::~Element()
    {
    
    }
    
    Element::Element( const Element& el ) : 
        m_Key( el.m_Key ),
        m_Value( new Element::Value( *el.m_Value ) )
    {
    
    }
    
    Element::Element( Element&& el ) : 
        m_Key(el.m_Key)
    {
        m_Value = std::move(el.m_Value);
        el.m_Value.release();
    }
    
    bool Element::operator < (const Element& o) const 
    { 
        return m_Key.m_KeyValue < o.m_Key.m_KeyValue; 
    }
    
    void Element::incrementValue() const
    {
        m_Value->value++;
    }
    
    int  
    Element::getValue() const
    {
        return m_Value->value;
    }
    
    void f()
    {
        std::set<Element> s;
    
        s.insert(Element(1,2));
        s.insert(Element(2,3));
    
        std::for_each(s.begin(), s.end(), [](const Element& s) { s.incrementValue(); });
    
        std::for_each(s.begin(), s.end(), [](const Element& s) 
        { 
            std::cout << s.getValue() << std::endl; 
    
        }); 
    }
    
    int 
    main()
    {
        f();
        return 0;
    }
    

    EDIT: To be honest however you must decide if the extra level of indirection makes sense or you would be better off using a map.

    0 讨论(0)
  • 2020-12-20 20:59

    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;
    
    0 讨论(0)
提交回复
热议问题