2011-11-25 8 views
6

Z jakiegoś powodu powtarzam iterację elementów klasy w std::set i chcę nieco zmodyfikować klucze, wiedząc, że zamówienie pozostanie niezmienione.Jak mogę poprawić ten projekt, który zmusza mnie do deklarowania funkcji członkowskiej const i deklarowania zmiennych zmiennymi?

Iteratory na std::setconst_iterators, ponieważ jeśli klucz zostanie zmodyfikowany, może to doprowadzić do złej kolejności, a tym samym do uszkodzenia zestawu. Jednak wiem na pewno, że moje operacje nie zmienią kolejności moich elementów w zestawie.

Na razie tu jest moje rozwiązanie:

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 
} 

Jak można zmodyfikować go (wiem, mogłem użyć std::map ale jestem ciekawy czy można zaproponować inne opcje), aby usunąć zmienne i const?

Dzięki

+2

Jaki jest konkretny powód, dla którego nie chcesz używać map? Czy to ze względu na układ pamięci (spójrz na alokatory?) Czy powody stylu kodu? – sehe

+0

@sehe: Szczególną przyczyną jest to, że chcę wiedzieć, czy istnieją inne opcje przed refaktoryzacją kodu. Nie wykluczam całkowicie przejścia na mapy. – Benoit

Odpowiedz

8

Nie możesz. Elementy zestawu muszą być stałe dla poprawności kontenera:

Wymusza to, aby zdać sobie sprawę z tego, że klucz klucz musi być niezmienny, lub niezmienniki struktury danych zostaną zerwane.

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

Jeśli chcesz zachować możliwość „Express” const ness w non-kluczowy element, podzielić go na pary i przechowywać je w mapie:

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

lub więcej elastycznie:

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

Dziękuję ... ale przechowywanie na mapie było tym, czego chciałem uniknąć, jak wyrażono w moim pytaniu :) Mimo to uważam za eleganckie zadeklarowanie struktury "wartości" wewnątrz klasy i utworzenie jej oddzielnie, więc +1. – Benoit

+1

@Benoit: gorszym rozwiązaniem jest użycie trybu pośredniego, poprzez zapisanie wskaźnika do części "value" możesz mieć element zwracający odwołanie do niego na życzenie, abyś miał poprawną "const" poprawność. To gorsze niż używanie "mapy", o ile mi wiadomo. W obliczu tego często wziąłem odpowiednik odpowiedzi 'sehe' i zdefiniowałem strukturę' Key' w moim 'elemencie', potem używam' std :: map '(duplikowanie informacji klucza). Ważne jest, aby część 'Kluczowa' 'elementu' była niemodyfikowalna ... –

1

Innym rozwiązaniem jest const_cast do rodzaju odniesienia:

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

Ale tak jak już powiedziałeś, nie powinieneś modyfikować elementów zestawu.

0

Jedną z możliwości może być fakturowanie wartościowej części Foo w 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; 
} 

EDYCJA: Szczerze mówiąc, musisz zdecydować, czy dodatkowy poziom pośredni ma sens, czy lepiej skorzystać z mapy.

Powiązane problemy