How to change a set element? - c++

I want to change the element in a set, so I used set<T>::iterator. However, the compiler argues "the element is const". Then I realized that set<T>::iterator is a const_iterator...
So, how I can change the element? Erase it and then insert a new one?

The elements of the set will be in sorted order. If you are allowed to modify an element, then this sorting order can not be maintained. Hence you can not modify the item. You need to erase the existing element and insert a new one.

Set elements are constant and may not be modified in place. Modification could change the ordering predicate without changing the position of the element which would violate the constraints of the data structure.
However now in the future (C++17), modifying the element without erasure is possible with the extract member function:
std::set<std::string> stringset{"a", "b", "c"};
auto node = stringset.extract(stringset.begin());
std::string& str = node.value();
str = "d";
stringset.insert(std::move(node));
There is still the cost of list operations but the object itself is not destroyed or copied.

EDIT: You cant add an element to a specific location in set. the set should be in the sorted order whatever operations you do. So you have to erase the specific element and insert the new element so that the ordering of set isnt lost.
Also read more about set!

Related

How to change some keys in a std::map using extract and insert

I've recently read this question about node_type, and I learned that if I need to modify the key of a specific node in a map, I could extract the node, modify the key and insert the node back in the map (like in this example).
Now, let's say I need to change the key of multiple nodes of this map, and to know which nodes I need to modify, I have to iterate over the values. I think I shouldn't insert the nodes back in the map while iterating (but maybe I'm wrong, see the end of the question), so I was thinking I could iterate over the map in a first time, extract the nodes I need to modify, store those in a vector, and insert the nodes back in a second time, when I'm finished iterating the map and modifying the nodes.
I suspect I could achieve that by storing the keys in a vector, not the nodes, but since the whole point is to change the keys, I thought it could be handy to use the nodes which seem to be made for that purpose.
So, basically, here's what I have so far:
std::vector</*don't know what to put here since node_type is undefined*/> tmp;
for (auto& it : myMap) {
if(it.second->needsToBeModified()){
tmp.push_back(myMap.extract(it.first));
}
}
I can't figure out what type that tmp vector would be.
As I said before, I may be wrong about not wanting to insert back the nodes while iterating, reading this answer stating that inserting or deleting in a map while iterating doesn't invalidate iterators (apart from the deleted entry). But this feels weird, what would happen if I insert a node in the map while iterating over it, would the inserted node come up in the iterating loop? Could that lead in an infinite loop?
The vector type is simple to address, since std:map specializations expose a type alias.
std::vector<std::map<K,V>::node_type> tmp;
The more involved point is the iteration. You cannot use a simple range-based for. That is because node extraction invalidates iterators to the extracted element. And that includes the iterator used behind the scenes for iteration. Instead, a more appropriate loop would be
for (auto it = myMap.begin() ; it != myMap.end();) {
if(it->second->needsToBeModified()){
auto out = it++;
tmp.push_back(myMap.extract(out));
}
else
++it;
}
Insertion while iterating is possible (since it doesn't invalidate iterators). But if the iteration order is important (even if only for debuggability), I think an auxiliary vector like you use is appropriate.
StoryTeller's answer is correct, however it would be nice if you wouldn't have to know how to spell out the whole type. There are several ways to simplify the declaration of tmp. The first is to avoid repeating the type of myMap, by using decltype():
std::vector<decltype(myMap)::node_type> tmp;
However, this still required you to know that the type of myMap contains another type named node_type. If you didn't know that, and still wanted to declare a vector that holds the result of myMap.extract(), then you can write:
std::vector<decltype(myMap.extract(myMap.begin()->first))> tmp;
As you can see, that's unfortunately quite a bit longer to type in, but you didn't have to use any knowledge about myMap, other than how you are actually using it inside the for-loop.

Automatically re-sorting tree

Is there a way to have a tree which automatically re-sorts its elements, or when calling some method (not when inserting new element)?
I would like to have a tree, i.e. a set, of small structures like this:
struct item
{
int value;
};
The set:
set<item> items;
auto p = items.insert(...);
And I would like to have a map holding iterators to the items held in set:
map<name, items::iterator> items_map;
items_map["abc"] = p.first;
After some time I would like to do lookup on the map and get the pointer to the item held in set to increase its value.
auto iter = items_map["abc"];
iter->value++;
Now I would like the set to automatically rearrange so that it reflects the updated value, I mean I would like the set to automatically sort itself (or after calling some method).
Is there a way to do so with current containers in STL?
I would like to do lookup on the map and get the pointer to the item held in set to increase its value.
Is there a way to do so with current containers in STL?
No. Elements of a std::set may not be modified. As such, the iterators are constant, and you couldn't use them to modify the elements.
The simple way to achieve similar effect would be to remove the old element, and insert a modified copy. However, any iterator that used to point to the old element will of course be invalidated by the removal of the element and will not point to the modified one.
Is there a way to have a tree which automatically re-sorts its elements, or when calling some method (not when inserting new element)?
Sure, but there is no such structure in the standard library.

Problems with C++ containers

I have a std::list in a C++ program, which contains objects of a Class A.
Lets say I have 10 objects in it. I have a reference to the 6th object stored, in another data structure say ref_6. Lets say I need to remove the 8th element from my list. To do this, I would use pop_front 8 times and store 8 objects in a vector and use push_front 7 times to insert the first 7 elements back in the list so now my resulting list would have 9 elemnts. Now i when i try to access the object stored in ref_6 , which was the 6th element , I cant do it. There is some garbage value in this reference.
I am assuming that when i do a pop and a push, the memory location of the same object changes . How do I deal with this ?
Why would you erase things in such a manner? D: It's not a stack. The entire point (and only point*) of a list is that you can remove any element in constant time. (Though finding it is linear.)
Just do this:
typedef std::list<T> list_type;
list_type mylist; // populate it
list_type::iterator iter = mylist.begin();
std::advance(iter, 8); // move to 8th item
mylist.erase(iter); // erase it
And no other iterators are invalidated. (Indeed, erasing an element invalidates any references to it.)
*You probably shouldn't even be using a list. Lists are nice when it comes to learning data structures, but they're pretty awful.
The list stores its elements in discontinous chunks of memory that get freed when the element is removed from the list. So the reference ( which is implemented simply as a pointer ) points to an element whose memory has been already freed.
Easier way to remove a given element from the list is to get the iterator pointing to it and use method
std::list::iterator = /*somehow get the iterator to the 8th element*/
yourList.erase(8th_element_iterator);
The first step ( getting the iterator to the 8th element ) can be done for example by getting the iterator of the begininning of the list and advancing it 7 positions forward:
std::list::iterator first_iter = yourList.begin();
std::list::iterator 8th_iter = std::advance(first_iter, 7);
Something smells fishy here... You are storing object of type T in a std::list<T> by value. You keep references to those objects in other places. Is that correct? If yes, I see several problems... Many manipulations of lists might invalidate stored references, since std::list<T> only guarantees a sequential order of values of elements of type T. If you want to store references to those elements in several places use std::tr1::shared_ptr<T> and std::list<std::shared_ptr<T> >. Then, you can safely remove or add (even reposition) elements in your list and the references kept in other places remain valid. Beware of storing std::list<T>iterators, the problem would be the same.
I am referring to your response. Sorry, I didn't get the account thing right...
Please consider the following:
std::list<A> tList;
A tA;
tList.push_back(tA);
assert(&tA == &tList.back()); // boom!
A *tAPtr = &tList.front();
tList.erase(tList.front());
// try to access tAPtr:
tAPtr->Foo(); // boom! (probably)
The point is that instances of A are stored by value (= copied), so what you are doing is inherently unsafe. Use std::list<std::tr1::shared_ptr<A> > instead!

stl vector assign vs insert

I understand the semantics of the 2 operations ,
assign- erases before replacing with supplied values.
insert - inserts values at specified location(allocates new memory if necessary).
Apart from this is there any reason to preffer one over the other?
Or to put it another way, is there any reason to use assign instead of insert.
If you wish to invoke the semantics of assign, call assign - if you wish to invoke the semantics of insert, call insert. They aren't interchangeable.
As for calling them on an empty vector, the only difference is that you don't need to supply an iterator to insert at when you call assign. There may be a performance difference, but that's implementation specific and almost certainly negligable.
assign and insert are only equivalent if the vector is empty to begin with. If the vector is already empty, then it's better to use assign, because insert would falsely hint to the reader that there are existing elements to be preserved.
assign() will blow away anything that's already in the vector, then add the new elements. insert() doesn't touch any elements already in the vector.
Other than that, if the vector you are modifying starts out empty, there is little difference.
insert() will insert the elements at a particular position. It can be a single value, or multiple values using first and last iterators. Its like inserting more elements in front of a position.
assign() will just assign the values starting at beginning till end as per input agruments "InputIterator first and InputIterator last ". This will remove all existing elements and instead populate the vector with new values as per range provided in its arguments.

Checking if element exists in unorderd_set before inserting it

I would like to ask if there is any point of checking if element exists in unorderd_set before inserting it? According to documentation:
Each element is inserted only if it is not equivalent to any other element already in the container (elements in an unordered_set have unique values).
So if I'm getting it correctly in my case:
Element *element = new Element;
//...
if ( my_set.find (element) == my_set.end() )
my_set.insert(element);
It is not required - correct? What actually happens if I try to insert element that is already in the set. Does it do exactly the same check that I am doing?
If you try to insert a value that is already present, the set will remain unchanged. The call will return a pair containing an iterator and Boolean. The Boolean will be false if the item was already present.
http://en.cppreference.com/w/cpp/container/unordered_set/insert
Note that in your case where the set contains pointers it will only prevent you from having duplicate pointers, not duplicate contents. For example,
if your Element object represented an element, like Oxygen, it might contain a value for the atomic number and a string for the name. If you only want oxygen to appear once, a set of pointers will not automatically do this.
An insert() on unorderd_set does a find() to see if the object exists and then inserts the object.
So in your code they are two find(), one explicit, and other inside the insert()