As I get it, C++17's node handles allow you to splice items inside and outside of some containers without actually moving the key and value objects. This is great, for example, if you need to keep some references to them in some other part of the program. In line of principle you can also keep a node handle outside of any container for some time, but still be able to access the key and value objects (and still without breaking references to it).
In my program it would make sense to directly create a node handle and later insert it in a map. Directly starting with the node handle (as opposed to create the key and value objects and inserting them in the map with the non-node handle insert call) would be great for me, because I might immediately take a reference to the value, which would remain valid even once the item is inserted in the map.
However, it doesn't appear that the is an appropriate constructor for std::map::node_type. In line of principle, I could just create an ad-hoc map, put a single item inside it and then splice it and destroy the map, but it seems a very un-C++-y way. Is there a better way? If not, is there a good reason not to do that?
The short answer to your question is - no, you have to use extract. Though there is problem with your idea how node handle works. You mistakenly think that reference to the value would remain valid after inserting the node into the new container - IT WILL NOT. Take look here:
Pointers and references to an element that are obtained while it is owned by a node handle are invalidated if the element is successfully inserted into a container.
Related
I am implementing a hashtable and am having trouble with the implementation. After literal hours of googling on this one thing, i've given up and was hoping to see of i could get any help here. The biggest issue is to do with the use of vectors in the HashTable(doesnt make sense to me, rather just use list<> but using it is required)
My main issue is to do with how to implement the insert function to add to the HashTable.
void HashTable::insert(ulint key,ulint value){ //insert data associated with key
HashNode nodeToAdd;
nodeToAdd.assign(key, value);
int index = hash_function(key);
this->table[index].push_back(nodeToAdd);
}
Now the issue im having is adding the HashNode to my HashTable.
for reference in HashTable, the field for the table is
typedef vector <list<HashNode> > Table;
Table *table;
So by my understanding
this->table[index].push_back(nodeToAdd);
is going to the vector HashTable[index], which at the index should be a list. and when it gets to that list, it should push_back the new node into the list.
However when compiled, i'm hit by an error(no matching function to call) and i don't understand why.
Your list stores objects of type HashNode, not type HashNode*.
So you need to decide which of those you want to use, and change the code accordingly.
If you want to keep storing HashNode, then your insert is wrong -- it should instead create the node on the stack and store it by value in the list.
If you want to store a pointer, then your Table type is wrong, and should instead be vector<list<HashNode*>> -- note it should be managed carefully since the pointers will not be automatically deleted.
Personally, I'd suggest you go with #1 and save yourself a whole lot of headaches. But if you insist on #2, then I suggest you stop using malloc and use new -- or better yet use std::unique_ptr or std::shared_ptr for automatic lifetime management.
Also noteworthy is your definition Table *table. This is baffling, since Table is a vector. Your insert function is dereferencing this pointer, expecting it to perhaps point to an array of Table values, when it's quite clear you actually think it's a vector. I'm pretty sure you don't want that to be a pointer.
Since I only just noticed that detail, I imagine that's the first source of your error, since table[index] is actually type Table, not type list<HashNode> and you were trying to call the non-existent function vector<list<HashNode>>::push_back(HashNode*).
I made a singly linked list and am creating an iterator for it but I am running into a problem when the element currently pointed to gets removed from the list. I have a class called list and within it, I have a nested class called iter. Currently iter's only field is a *currentNode pointer (it gets passed in a node in the constructor).
So how do I handle when the element that it is pointing to gets deleted. My first thought was that I should try to handle that in my list's remove() functions but I don't think it is possible to / don't know how to tell from the list's point of view whether the current element is being pointed to by the iterator (the iterator's currentNode pointer seems to not be in the scope of the class based on the compiler's errors). My second thought was to try to handle it from the iterator itself, but I can't figure out how to even begin to go about that.
I'm sorry if the answer is really simple. I couldn't find the answer anywhere online which makes me think that it might be but I just can't figure out how to handle this for the life of me. Thank you!
The list does not need to know anything about the iterator or its content. When the iterator removes its current node from the list, the iterator is invalidated. Even iterators in most standard STL container behave that same way. If you look at std::list, for instance, its erase(iterator) method returns a new iterator that represents the next node in the list following the node that was removed. You can do the same in your own remove() method. Otherwise, remove() would have to update the iterator to point at a different node, but that tends to go against how iterators typically operate.
You have 2 options.
If you dereference an iterator to an object that doesn't exist it is undefined behavior. The standard library does it and it is accepted. Also it is easy to implement since you don't need to handle that case.
Keep a std::shared_ptr to the element. That way the value does not get deleted as long as the iterator exists. You have to pay a performance penalty for doing this even if you do not use the feature. Additionally it can be perceived as hiding a bug.
I have a pointer p (not an iterator) to an item in a list. Can I then use p to delete (erase) the item from the list? Something like:
mylist.erase(p);
So far I have only been able to do this by iterating through the list until I reach an item at the location p, and then using the erase method, which seems very inefficient.
Nope, you'll have to use an iterator. I don't get why getting the pointer is easier than getting an iterator though...
A std::list is not associative so there's no way you can use a pointer as a key to simply delete a specific element directly.
The fact that you find yourself in this situation points rather to questionable design since you're correct that the only way to remove the item from the collection as it stands is by iterating over it completely (i.e. linear complexity)
The following may be worth considering:
If possible, you could change the list to a std::multiset (assuming there are duplicate items) which will make direct access more efficient.
If the design allows, change the item that you're pointing to to incorporate a 'deleted' flag (or use a template to provide this) allowing you to avoid deleting the object from the collection but quickly mark it as deleted. Drawback is that all your software will have to change to accommodate this convention.
If this is the only bit of linear searching and the collection is not big (<20 items say.) For the sake of expediency, just do the linear search as you've suggested but leave a big comment in the code indicating how you "completely get" how inefficient this is. You may find that this does not become a tangible issue in any case for a while, if ever.
I'm guessing that 3 is probably your best option. :)
This is not what I advice to do, but just to answer the question:
Read only if you are ready to go into forbidden world of undefined behavior and non-portability:
There is non-portable way to make an iterator from T* pointer to an element in a list<T>. You need to look into your std library list header file. For Gnu g++ it includes stl_list.h where std::list definition is. Most typically std::list<T> consists of nodes similar to this:
template <class T>
struct Node {
T item;
Node* prev;
Node* next;
};
Having pointer to Node<T>::item you can by using offsetof calculate this node pointer. Be aware that this Node template could be the private part of std::list so you must hack this - let say by defining identical struct template with different name. std::list<>::iterator is just wrapper over this node.
It cannot be done.
I have a similar problem in that I'm using epoll_wait and processing a list of events. The events structure only contains a union, of which the most obvious type to use is void * to indicate which data is relevant (including the file descriptor) that was found.
It seems really silly that std::list will not allow you to remove an element via a pointer since there is obviously a next and previous pointer.
I'm considering going back to using the Linux kernel LIST macros instead to get around this. The problem with too much abstraction is that you have to give up on interoperability and communication with lower level apis.
I'm working on a UI. The base class for a UI component is UILayout, and the entire UI is a tree of UILayout objects, with the root being a UILayout representing the entire screen. In order to contain this hierarchy, any given UILayout has a vector mChildren of boost::shared_ptr<UILayout>.
A UIManager object takes care of Updating the entire hierarchy of UILayouts. Each call to Update iterates over the vector mChildren, calling Update on each child recursively.
Because changing the shape of the vector would invalidate those iterators, adding and removing entries from mChildren is confined to the ResizeChildren method. When components need to be added or removed, they are added to one of two vectors, mChildrenPendingAddition and mChildrenPendingRemoval. Immediately before the Update loop, ResizeChildren is called, and mChildren is updated accordingly. (Please stop me if this is an asinine way of handling this particular problem.)
I'm getting an exception when I attempt to remove from mChildren all entries which are also contained in mChildrenPendingRemoval. From UILayout::ResizeChildren():
mChildren.erase(remove_if(mChildren.begin(), mChildren.end(),
IntersectsWithChildrenPendingRemoval(this)), mChildren.end());
IntersectsWithChildrenPendingRemoval's comparison function calls this->ChildrenPendingRemovalContains(HUILayout ly), which does the following:
return (find(mChildrenPendingRemoval.begin(), mChildrenPendingRemoval.end(),
ly) != mChildrenPendingRemoval.end());
That line sometimes fails the debug assertion vector iterators incompatible. There are plenty of existing questions on this error, but it seems like it normally indicates that two iterators from different containers are being compared. But here, that's clearly not the case, right? What else could cause this problem?
Relevant source code:
Class and predicate definition
Implementation of the offending methods
This is a plugin that I'm developing for a multi-threaded application. The fact that the problem crops up at very rare and random intervals leads me to believe it has something to do with the fact that the plugin is running in separate threads, but all of these methods are called from a single function, squarely in a single thread, and mChildren is not accessed or modified in any other thread.
Please stop me if this is an asinine way of handling this particular problem
Why don't you work on a copy of the collection, and swap it in at once:
std::list<X> copy(mChildren);
copy.insert(...);
copy.remove(...);
copy.insert(...);
// at once:
std::swap(copy, mChildren);
Further thoughts:
it is in general not very convenient to keep iterators into mutable containers around for any period of time
Since this is a container of smart-pointers... why don't you pass the smart-pointers themselves around if you needed to keep 'pointers' to elements? (Of course, that wouldn't enable iteration, but that is IMO not a very healthy desire anyway)
Just use indices instead of iterators.
Iterators are overrated. They are nothing more than general pointers. The best way to manage items in a vector was and is allways to use indices. If you want to use iterators, try std::list, in this case iterators won't be invalidated even if you add elements to the list or remove them.
I implemented the Visitor pattern in C++ using a STL-like iterator for storing the Visitor's current position in the container. Now I would like to change the container while I iterate over it, and I'm especially interested in deleting items from the container, even the one I'm currently visiting.
Now obviously this will invalidate the Visitors internal iterator, because it was pointing to exactly this item. Currently, I store a list of all iterators in the container and update them, as soon as anything is added to or removed from the list. So in a way this is similar to the Observer pattern applied to the iterator (as Observer) and the list (as Observable).
Alternatively I considered having the visitor() methods return some hint to the Visitor about what happend to the current item and how to proceed iterating, but that doesn't sound like such a good idea either, because the visit() implementation shouldn't really care about finding the next item.
So, my question is: What's the best way to keep a visitor working, even when items are added to the container or removed from it.
Regards,
Florian
Update: There's one visitor running over the container, but inside of the visit() method any number of additional iterators might be used on the same container. I want the visitor to continue with the remaining items in the container, even after we returned from a call to visit() in which any one of the items in the container got deleted.
When mutating the container during traversal, iterators are hazardous, at best. It is safest to use an index and walk backwards.
I think your (first) implementation is pretty good if there are not so many iterators and delete operations. If this would be the case I would use a mark and sweep like algorithm like Eddy recommended. Also, I think the latter is easier and thus less error prone. Don't forget to skip nodes which are marked for deletion.
On the other hand, if there are cases besides 'delete' where your iterators need to be updated, stick to your current implementation.
In these cases, if copying my container is not expensive, I just copy it and iterate on the copy. The original container holds objects by shared_ptr while the copy just holds weak_ptr.