どのようにしてメンバ関数constを宣言し、変数を可変に宣言するようにするこの設計を改善できますか?

何らかの理由で、クラスの要素を std :: set で繰り返し処理していて、順序が変更されないことを知ってキーを少し変更したいと考えています。

std :: set のイテレータは、キーが変更された場合、順序が正しくないために破損してしまう可能性があるため、 const_iterators です。しかし私は、私の操作がセット内の要素の順序を変えないことを確かに知っています。

今のところ私の解決策はここにあります:

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
}

あなたはそれを変更しますか(私は std :: map を使うことができますが、他のオプションを提案できるかどうか不思議です)

ありがとう

6
マップを使用したくない具体的な理由は何ですか?メモリのレイアウト(アロケータを見ますか?)やコードスタイルの理由からですか?
追加された 著者 sehe,
@sehe:具体的な理由は、コードをリファクタリングする前に他のオプションが存在するかどうかを知りたいということです。私はマップへの切り替えを完全に排除するわけではありません。
追加された 著者 Benoit,

3 答え

あなたはできません。 set要素は、コンテナの正確さのためにconstでなければなりません:

重要な部分が不変でなければならないこと、またはデータ構造の不変量が壊れてしまうことを認識させる必要があります。

struct element 
{
     std::string key_part;//const in the set

     bool operator<(const element&o) const { return key_part

キー以外の部分で定数を '表現する'可能性を保持したい場合は、ペアに分割してマップに格納します。

std::map mapped;

より柔軟に:

struct element 
{
     std::string key_part;//const in the set

     bool operator<(const element&o) const { return key_part mapped;
8
追加された
@Benoit:より悪い解決策は、間接参照を使用することです。 "value"部分へのポインタを格納することで、正しい const の正しさを持つことを望む上で、要素への参照を返すことができます。私が心配している限り、 map を使うよりも悪いです。これに直面したとき、私は Element sehe の答えをとり、 Key 構造体を定義した後、 code> std :: map (キー情報を複製する) 要素 Key 部分は変更不可能であることが重要です。
追加された 著者 Matthieu M.,
ありがとう...しかし、マップに格納する私の質問で表現された避けるためにしたかった:)まだ、私はクラスの内部に "値"構造体を宣言し、別々にそれをインスタンス化するエレガントなので、+1。
追加された 著者 Benoit,

もう1つのオプションは、参照型に const_cast することです。

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

しかし、既に述べたように、setの要素を変更すべきではありません。

1
追加された

1つの可能性は、PimplのFooの値の部分を除外することです。

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;
}

編集:正直言ってしかし、あなたは間接指示の余分なレベルが理にかなっているか、あなたがマップを使用する方が良いかどうかを判断する必要があります。

0
追加された