Станд. C++:: обновление набора утомительно: Я не могу изменить элемент на месте

Я нахожу операцию обновления на std::set утомительный с тех пор на cppreference нет такого API. Таким образом, то, что я в настоящее время делаю, является чем-то вроде этого:

//find element in set by iterator
Element copy = *iterator;
... // update member value on copy, varies
Set.erase(iterator);
Set.insert(copy);

В основном итератор возвращается Set a const_iterator и Вы не можете изменить его значение непосредственно.

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

65
задан Dev Null 17 January 2019 в 00:12
поделиться

4 ответа

set возвращает const_iterators (в стандарте указано set :: iterator равен const , и что set :: const_iterator и set :: iterator могут на самом деле быть того же типа - см. 23.2.4 / 6 в n3000.pdf), потому что это заказанный контейнер. Если он вернет обычный итератор , вам будет разрешено изменять значение элементов из-под контейнера, потенциально изменяя порядок.

Ваше решение - это идиоматический способ изменения элементов в наборе .

72
ответ дан 24 November 2019 в 15:24
поделиться

Обновление: Хотя на данный момент верно следующее, поведение считается дефектом и будет изменено в следующей версии стандарта.Как очень грустно.


Есть несколько моментов, которые сбивают с толку ваш вопрос.

  1. Функции могут возвращать значения, классы - нет. std :: set - это класс, поэтому он не может ничего возвращать.
  2. Если вы можете вызвать s.erase (iter) , то iter не является const_iterator . erase требует неконстантного итератора.
  3. Все функции-члены std :: set , которые возвращают итератор, возвращают неконстантный итератор, если набор также не является константой.

Вам разрешено изменять значение элемента набора, если обновление не изменяет порядок элементов. Следующий код компилируется и отлично работает.

#include <set>

int main()
{
    std::set<int> s;
    s.insert(10);
    s.insert(20);

    std::set<int>::iterator iter = s.find(20);

    // OK
    *iter = 30;

    // error, the following changes the order of elements
    // *iter = 0;
}

Если ваше обновление изменяет порядок элементов, вам придется стереть и снова вставить.

8
ответ дан 24 November 2019 в 15:24
поделиться

В простом случае это можно сделать двумя способами:

  • Вы можете использовать mutable для переменной, не являющейся частью ключа
  • Вы можете разделить свой класс на Key Value пара (и использовать std :: map )

Теперь вопрос для сложного случая: что происходит, когда обновление фактически изменяет ключ часть объекта? Ваш подход работает, хотя я признаю, что это утомительно.

24
ответ дан 24 November 2019 в 15:24
поделиться

Вместо этого вы можете использовать std :: map . Используйте часть Element , которая влияет на порядок ключей, и поместите весь Element в качестве значения. Будет некоторое незначительное дублирование данных, но вы получите более легкие (и, возможно, более быстрые) обновления.

8
ответ дан 24 November 2019 в 15:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: