Как я могу удалить элемент из набора на С++, не удаляя его?

Используя std::set в C++, единственный способ удалить элемент из набора — это использовать метод стирания. Это удаляет рассматриваемый элемент, чего я не хочу. Единственный способ, которым я могу придумать, чтобы удалить элемент из набора, не удаляя его, - это создать новый набор и итеративно добавлять в него все элементы старого набора, следя за тем, чтобы не добавить элемент, который необходимо удалить из набор, а затем удаление старого набора.

Есть ли более чистый способ сделать это?

Запутался, потому что: и метод (1), и метод (2) удаляют рассматриваемый элемент.

Richard Critten 11.04.2019 00:18

Скажем, вы создаете новый набор. Как бы вы переместили в него любые существующие предметы? Если вы имеете в виду буквальное использование семантики перемещения, это будет означать удаление старых элементов, а это то, что вы сказали, что не хотите делать. Если бы не семантика движения, в каком смысле она вообще была бы движущейся?

David Schwartz 11.04.2019 00:22

Было бы очень полезно понять, почему вы не хотите, чтобы исходный объект был удален. Это имеет огромное значение, если это потому, что, например, у вас где-то спрятаны ссылки на него, а не потому, что это дорого для создания.

David Schwartz 11.04.2019 00:30

Непонятно, что вы имеете в виду. Вы можете скопировать элемент в другое место (например, вектор), а затем удалить его из набора. Вы по-прежнему удаляете исходное значение, но у вас есть копия этого значения в другом контейнере. Это то, что вы имеете в виду?

Martin York 11.04.2019 00:36

Возможный дубликат stackoverflow.com/questions/45030932/…

Galik 11.04.2019 01:07
tio.run/…
jxh 11.04.2019 02:42
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
6
2 264
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы не можете удалить элемент из набора, не удалив его. Наборы владеют своими элементами. Если элемент удален из набора, он больше не существует. Если вы хотите иметь возможность удалить что-то, не удаляя это, не добавляйте это в набор.

Представьте, что у вас есть int x[5]; x[2]=2;. Как вы можете получить x[2] из массива? Что бы это вообще значило? Конечно, вы можете создать новое целое число с тем же значением int j = x[2];. Но это новый объект (с тем же значением), который не продлевает срок службы существующего объекта.

В зависимости от вашей внешней проблемы может быть решение. Например, вы можете добавить std::unique_ptr к объекту в набор, а затем уничтожить этот std::unique_ptr, не уничтожая объект, на который он указывает, создавая новый std::unique_ptr для того же базового объекта.

За исключением того, что вы можете - вы можете использовать extract + std::move для передачи права собственности на элемент

Alecto Irene Perez 11.04.2019 00:22

@JorgePerez Это не передает право собственности на элемент. Это удаляет существующий элемент при создании нового элемента с тем же значением, что и существующий. (Если вы не хотите поместить старый элемент в контейнер того же типа.)

David Schwartz 11.04.2019 00:23

Если существующий элемент не владеет какими-либо ресурсами, то его копирование не имеет значения. Если он владеет ресурсами, то это передаст право собственности на ресурсы.

Alecto Irene Perez 11.04.2019 00:25

@JorgePerez Копирование элемента, которому не принадлежат какие-либо ресурсы, и уничтожение оригинала делает недействительными все указатели и ссылки на него. Таким образом, вы не можете сказать, что это «не имеет значения».

David Schwartz 11.04.2019 00:26

Этот ответ описывает единственный разумный вариант.

J.R. 11.04.2019 00:48

Перемещение объекта из набора

Вы можете использовать extract, чтобы удалить соответствующий узел из набора. Это дает вам дескриптор узла. Когда у вас есть ручка, вы можете переместить предмет из ручки.

template<class T>
T find_and_remove(std::set<T>& s, T const& elem) {
    auto iterator = s.find(elem); 
    if (iterator == s.end() {
        throw std::invalid_argument("elem not in set"); 
    }
    // Remove element, return node handle
    auto node_handle = s.extract(iterator);
    return std::move(node_handle.value());
}

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

template<class T>
T remove_from_set(std::set<T>& s, std::set<T>::iterator it) {
    return std::move(s.extract(it).value());
}

Перемещение значения передает право собственности на любые ресурсы, принадлежащие этому значению. Например, если набор содержит строку, содержимое строки не будет удалено, и любые итераторы строки не будут признаны недействительными.

Предостережение заключается в том, что если у вас были указатели или ссылки на объект, когда он еще был в наборе, они будут недействительны.

Извлечение самого объекта без перемещения и без аннулирования каких-либо указателей или ссылок на объект

Это менее распространенный случай, но если у вас есть ссылка или указатель на объект в наборе, вы можете сделать это.

Опять же, мы можем использовать функцию extract:

auto node_handle = s.extract(my_object);

Или:

auto node_handle = s.extract(my_iterator); 

Вы можете получить доступ к сохраненному объекту с помощью node_handle.value(), который возвращает ссылку на объект. Объект не будет удален до тех пор, пока не будет удален node_handle, и если вам нужно еще больше продлить его время жизни, вы можете вернуть node_handle из функции без удаления объекта.

Ваш remove_from_set возвращает новый объект с тем же значением, что и исходное значение, и уничтожает оригинал — именно то, что ОП сказал, что они не хотят делать.

David Schwartz 11.04.2019 00:25

Если исходное значение поддерживает семантику перемещения, уничтожение исходного значения не имеет значения. Любые ресурсы будут перенесены без удаления.

Alecto Irene Perez 11.04.2019 00:27

Как я уже сказал в своем ответе на ваш комментарий к моему ответу, это просто ложь. Это может иметь большое значение, например, если где-то спрятан указатель или ссылка на него. Мы не знаем, почему ОП не хочет, чтобы исходный объект удалялся, но они сказали, что этого хотят избежать.

David Schwartz 11.04.2019 00:27

Ваше последнее редактирование делает ваш ответ опасно неверным. Что, если, например, строка использует оптимизацию короткой строки, а итератор содержит указатель на буфер внутри самого объекта std::string.

David Schwartz 11.04.2019 00:29

Я обновил свой ответ, уточнив и проведя различие между «переместить из набора» и «удалить, не делая недействительными какие-либо существующие указатели или ссылки». Довольны ли вы изменениями?

Alecto Irene Perez 11.04.2019 00:33

Стандартные контейнеры C++ создают "внешние объекты", которые содержат элемент для контейнера (имеет отношение). Например, элемент принадлежит массиву, управляемому вектором, или элемент принадлежит узлу rb-дерева, управляемому набором. При удалении элемента из контейнера вы должны позволить «внешнему объекту» стать доступным для хранения другого элемента. Таким образом, для сохранения элемента требуется как минимум копия в другое место в памяти. Я думаю, что ОП хочет, чтобы контейнер вмещал std::reference_wrapper.

jxh 11.04.2019 00:34

Другие вопросы по теме