LINUX.ORG.RU

Как сделать итератор от set не константным по умолчанию?

 , ,


0

1

Можно ли что-либо сделать с итератором std::set что бы можно было итерировать значения без конст-признака по умолчанию?


class Compare {
   bool operator()(const MyClass& v1, const MyClass& v2) const {
       return v1.constKey() < v2.constKey();
   }
};

std::set<MyClass,Compare> mySet;

...
for (MyClass& item: mySet) // <- error here
   ...;
Или без оберток не обойтись?

Защита от выстрела в ногу. Вдруг после модификации объекта у него ключ поменяется? Тогда std::set сломается. Он же не узнает об изменении.

Используй std::unordered_map, ну или просто std::map на худой конец.

ox55ff ★★★★ ()

Нет, и тебе это не нужно. Менять значение inplace ты не можешь потому что тогда set сломается. А если менять значение тебе не нужно, то не нужен и мутабельный итератор.

slovazap ★★★★★ ()
Ответ на: комментарий от ox55ff

Защита от выстрела в ногу. Вдруг после модификации объекта у него ключ поменяется?
Менять значение inplace ты не можешь потому что тогда set сломается.

А для кого я специально написал реализацию класса Compare? Конечно же для КэПов. Ой, теперь и я КэП..

victor79 ()
Последнее исправление: victor79 (всего исправлений: 1)
Ответ на: комментарий от victor79

Спасибо, Кэп, я видел. Но какое это имеет отношение к твоему вопросу?

std::set - дерево (как правило). При вставке в контейнер, элемент будет расположен в соответствующем месте согласно ключу. Потом ты через мутабельный итератор меняешь содержимое элемента, что может привести к изменению ключа, и теперь элемент находится не в том месте дерева –> std::set развалился.

Контейнер не знает, что ключ поменялся и не может перестроить дерево. Ты там видел коллбеки для для информирования контейнера, что ключ поменялся? Я нет. Потому что их нет.

Ты хочешь неправильного.

ox55ff ★★★★ ()

Если ты у item хочешь вызвать неконстантный метод, который гарантированно никак не может поменять ключ объекта, то можно попробовать const_cast

annulen ★★★★★ ()
Ответ на: комментарий от victor79

А для кого я специально написал реализацию класса Compare? Конечно же для КэПов. Ой, теперь и я КэП..

А при чём тут реализация Compare? Имея мутабельный итератор ты можешь легально изменить состояние класса так что результат сравнения изменится, а следовательно нарушатся инварианты set и начнётся UB.

slovazap ★★★★★ ()
Ответ на: комментарий от annulen

Если хочется, чтобы все было некостыльно и с защитой от дурака, то можно в set вместо MyClass положить std::pair<Key, MyClass> и дальше уже для second делать const_cast и насиловать его как хочешь

annulen ★★★★★ ()
Ответ на: комментарий от annulen

гарантированно никак не может поменять ключ объекта

А через неделю коллега Вася, который не знает про нюансы, внесёт изменение в код, меняющее ключ и всё пойдёт по половому органу. Не надо так делать.

ox55ff ★★★★ ()
Ответ на: комментарий от annulen

Да, вариант с pair безопаснее

Только «вариант с pair» - это обычный map, только сделанный через жопу.

slovazap ★★★★★ ()
Последнее исправление: slovazap (всего исправлений: 1)
Ответ на: комментарий от annulen

то можно в set вместо MyClass положить std::pair<Key, MyClass> и дальше уже для second делать

Да, но это уже получится обычный мап, и это по прежнему не меняет поведение итератора.

то можно попробовать const_cast

это то же не работает, для set типы iterator && const_iterator это одно и то же, о чем упоминается здесь.

В итоге сделал обертку, с использованием которой циклы стали выглядеть цивильно:

template <class Itr, class Data>
struct TmplItr1 {

    TmplItr1(const Itr& ii) : itr(ii) {}

    bool operator==(const TmplItr1&o) const { return itr == o.itr; }
    bool operator!=(const TmplItr1&o) const { return itr != o.itr; }

    Data &operator*() const { return const_cast<Data&>(*itr); }

    void operator++() { ++itr; }

private:
    Itr itr;
};

// и класс который использует этот итератор, в сильно упрощенном виде:
template <class Key, class Data>
struct IdxNode1 {

    struct Cmp {
        bool operator()(const IdxNode1& v1, const IdxNode1& v2) const {
            return v1.p_key < v2.p_key; } };

    using Set = std::set<IdxNode1,Cmp>;
    using iterator = TmplItr1<typename Set::iterator, IdxNode1>;
    
    iterator begin()          { return setNextLevel.begin(); }
    iterator end()            { return setNextLevel.end(); }

    Data data;
    Set setNextLevel;

private:
    Key p_key;
};

Оператор копирования p_key не меняет, и т.д.

victor79 ()
Последнее исправление: victor79 (всего исправлений: 1)
Ограничение на отправку комментариев: только для зарегистрированных пользователей