Reputation: 143
Using std::set in C++, the only way I can find to remove an item from a set is to use the erase method. This deletes the item in question, which I don't want to happen. The only way I can think of to remove an item from a set without deleting it would be to create a new set and add all the items of the old set to it iteratively, making sure to not add the item that needs to be removed from the set, then deleting the old set.
Is there a cleaner way to do this?
Upvotes: 5
Views: 3865
Reputation: 182759
You can't remove an item from a set without deleting it. Sets own their members. If the member is removed from the set, it doesn't exist anymore. If you want to be able to remove something without deleting it, don't add it to a set.
Imagine if you have int x[5]; x[2]=2;
. How can you get x[2]
out of the array? What would that even mean? You can, of course, construct a new integer with the same value, int j = x[2];
. But that's a new object (with the same value) that is not extending the life of the existing object.
Depending on what your outer problem is, there might be a solution. For example, you could add a std::unique_ptr
to an object into a set and then you can destroy that std::unique_ptr
without destroying the object it points to be constructing a new std::unique_ptr
to the same underlying object.
Upvotes: 6
Reputation: 10740
You can use extract
to remove the corresponding node from the set. This gives you a handle to the node. Once you have the handle, you can move the item out of the handle.
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());
}
Alternatively, if you already have the iterator to the node, you can write it like this:
template<class T>
T remove_from_set(std::set<T>& s, std::set<T>::iterator it) {
return std::move(s.extract(it).value());
}
Moving the value transfers ownership of any resources owned by the value. For example, if the set contains a string, the contents of the string won't be deleted, and any iterators to the string won't be invalidated.
The caveat of this is that if you had pointers or references to the object from when it was still in the set, these will be invalidated.
This is the less common case, but if you had a reference or pointer to the object in the set, you may want to do this.
Again, we can use the extract
function:
auto node_handle = s.extract(my_object);
Or:
auto node_handle = s.extract(my_iterator);
You can access the stored object with node_handle.value()
, which returns a reference to the object. The object won't be deleted until the node_handle
is deleted, and if you need to extend it's lifetime further, you can return the node_handle
from a function without the object being deleted.
Upvotes: 2