Comment puis-je améliorer cette conception qui m’oblige à déclarer une fonction membre const et à déclarer des variables mutables?

Pour une raison quelconque, j’itère sur des éléments d’une classe dans un std::set et souhaite modifier légèrement les clés, sachant que l’ordre ne sera pas modifié.

Les iterators sur std::set sont des const_iterators car si la clé est modifiée, un ordre incorrect peut en résulter et donc une corruption de l’ensemble. Cependant, je sais avec certitude que mes opérations ne changeront pas l’ordre de mes éléments dans l’ensemble.

Pour le moment, voici ma 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 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 } 

Comment le modifierais-je (je sais que je pourrais utiliser std::map mais je suis curieux de savoir si vous pouvez suggérer d’autres options) pour supprimer mutable et const?

Merci

Tu ne peux pas. Les éléments de set doivent être const pour la correction du conteneur:

Cela vous oblige à réaliser que la partie clé doit être immuable, sinon les invariants de structure de données seraient brisés.

 struct element { std::ssortingng key_part; // const in the set bool operator<(const element&o) const { return key_part 

Si vous souhaitez conserver la possibilité d'exprimer la constance dans la partie non clé, séparez-la en paires et stockez-la dans une carte:

 std::map mapped; 

ou, de manière plus flexible:

 struct element { std::ssortingng key_part; // const in the set bool operator<(const element&o) const { return key_part mapped; 

Une autre option consiste à const_cast vers un type de référence:

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

Mais comme déjà dit, vous ne devriez pas modifier les éléments de l’ensemble.

Une possibilité pourrait être de factoriser la partie valeur de Foo dans un 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 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 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: Pour être honnête, vous devez toutefois décider si le niveau supplémentaire d’indirection est logique ou si vous feriez mieux d’utiliser une carte.