Изменить элемент в наборе с помощью итератора

0

Я хочу изменить элемент в наборе (QSet):

for(PSet::iterator pIt = P.begin(); pIt != P.end(); ++pIt)
  pIt->xp = 0;

Компилятор не позволит мне это сделать ("C3892:" var ": вы не можете назначить переменную const). Кажется, что набор итераторов всегда постоянен из-за опасения, что изменение элемента может повредить его правильное положение в наборе.

В моем случае PSet - это набор структур, для которых я определил свою собственную хеш-функцию:

struct P
{
  P(int id, const Data_t& data)
     :xp(_INFINITY_)
     ,id(id)
     ,data(data){}

  int xp;  
  const int id;
  const Data_t data;
};

Моя хеш-функция не учитывает неконстантный член xp, поэтому мое вышеприведенное назначение должно быть абсолютно безопасным относительно порядка элементов в наборе. Я не хочу удалять элемент и повторно вставлять его, поскольку производительность на самом деле является проблемой.

Думаю, я мог бы использовать const-casts, но это ухудшило бы мою читаемость кода и выглядело бы как неприятный хак. Есть ли у меня другие варианты?

Теги:
set
compiler-errors

2 ответа

4
Лучший ответ

Изменение элементов QSet недопустимо, поскольку оно может нарушить заданную внутреннюю структуру контейнера, изменив один из хэш-результатов.

Что касается стандартных ассоциативных контейнеров, то это в стандартном разделе § 23.2.4

итератор ассоциативного контейнера относится к категории двунаправленного итератора. Для ассоциативных контейнеров, где тип значения совпадает с типом ключа, оба итератора и const_iterator являются постоянными итераторами. Не указано, является ли итератор и const_iterator одним и тем же типом.

Если вы знаете, что ваша модификация не влияет на элемент отсортированной позиции, использование const_cast является одним из тех редких случаев, когда это нормально.

Однако, идиоматическим способом является использование подсказки:

  1. Найдите элемент, который нужно изменить
  2. Сделайте копию элемента
  3. Изменить копию
  4. Удалить элемент
  5. Вставьте копию, используя вставку подсказки, когда она доступна (нет для QSet)

Пример:

  s.erase(original);
  // modify p ...
  s.insert(copy, hint);

Заметки:

  1. То же самое относится и к другим ассоциированным контейнерам.
  2. Этот вопрос широко обсуждается в "Эффективном STL, пункт 22", Скот Мейерс.
  3. Иногда рекомендуется считать std::vector заменой std::set
  • 0
    +1: правильный ответ!
  • 0
    Вставка QSet не дает подсказки. Вероятно, потому что это основано на хэш-таблице. Как насчет использования mutable для члена, который я хочу изменить, вместо нескольких const_casts в разных местах?
Показать ещё 1 комментарий
0

Элементы set не могут быть дублирующими. Если вам будет разрешено изменить элемент, тогда вся эта логика не будет сохранена. Поэтому вам нужно удалить существующий элемент и вставить новый.

Поэтому, если вы не используете const_cast, нет другого способа сделать это. Однако, как уже упоминалось, это может нарушить set, позволяя ему иметь потенциальные повторяющиеся значения.

Вот как это сделать для std::set<int> (я знаю, что это не набор QT, но логика должна быть одинаковой):

std::set<int> mySet;
mySet.insert(3);
mySet.insert(4);
for (auto i = mySet.begin(); i != mySet.end(); i++)
{
    int* elem = const_cast<int*>(&*i);
    *elem = 6;
}
  • 0
    QSet - это набор на основе хеш-таблицы. Это элементы не отсортированы.
  • 0
    ОК, спасибо за хэдсэп. Я оставлю только релевантные вещи в своем ответе и проясню отсортированные связанные :)
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню