2011-11-25 34 views
6

由於某種原因,我在std::set中迭代了一個類的元素,並且想要稍微修改這些鍵,並知道該順序將保持不變。我該如何改進這種強迫我聲明一個成員函數const並聲明變量可變的設計?

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

你將如何修改它(我知道我可以使用一個std::map,但我很好奇,你是否可以建議其他選項),以去除可變和const?

感謝

+2

你不想使用地圖的具體原因是什麼?是因爲內存佈局的原因(看分配器?)還是代碼風格的原因? – sehe

+0

@sehe:具體原因是我想知道在重構代碼之前是否存在其他選項。我並不完全排除切換到地圖。 – Benoit

回答

8

你不能。設置元素對於容器正確性必須是const:

它強制您意識到關鍵部分需要是不可變的,否則數據結構不變量將被破壞。

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

如果你想保留的可能性,「表達」在非關鍵部分常量性,把它分解出來成對並將其存儲在一個地圖:

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

,或者更靈活:

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

謝謝......但在地圖中存儲是我想要避免的問題:)儘管如此,我發現在類中聲明一個「值」結構並單獨實例化它是優雅的,所以+1。 – Benoit

+1

@Benoit:更糟糕的解決方案是使用間接方式,通過存儲指向「值」部分的指針,您可以讓元素返回對其的引用,希望您具有正確的「const」正確性。就我而言,它比使用「地圖」更糟糕。當面對這個問題時,我經常拿着'sehe'的答案對應並在我的'Element'中定義了一個'Key'結構,然後我使用了一個'std :: map '(複製關鍵信息)。元素的'Key'部分是不可修改的,但是重要的是... –

1

另一種選擇是const_cast爲引用類型:

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

但正如sehe已經說過的,你不應該修改set的元素。

0

一種可能性可能是在一個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<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; 
} 

編輯:說實話,但你必須決定是否額外的間接水平是有道理的,或者你會更好地使用地圖。