c++ map/set iterator not dereferencable using .find - c++

I've relatively new to using maps, and I'm currently getting the Debug Assertion Failed Expression: map/set iterator not dereferencable
When I hit retry it brings me to this section of code:
auto temp = mOpenMap.find(currentNode);
temp->second = false;
I assume this has to do with the .find(currentNode) returning the end of the map, as it didn't find it, but the concerning part here is that doing my manual debugging I found that the only Node in the map indeed contained the exact parts of the currentNode I had it search for.
My map is this:
std::map<PathNode*, bool> mOpenMap
Optimistically what I would like it to do is search for the row and column to ascertain that it is looking at a node that has already been searched so that I can set the accompanying boolean to false.
What I'm wondering, is how do maps generally search for objects? Or better yet, how can I go about making the map search with a custom search?

You should check std::map::find does find the element before dereference the iterator:
auto temp = mOpenMap.find(currentNode);
if (temp != mOpenMap.end()) // check temp is pointing to underneath element of a map
{
temp->second = false;
}

If all you're doing is tracking the presence of some PathNode or other, you'd be better off using a std::set.
As for a custom search, both std::map and std::set work with a collection of values, ordered by a comparator. That comparator can be specified as the second template type when the map or set is defined. If omitted, that comparator defaults to std::less, which simply compares the objects with the less than operator, operator<. As written, your map mOpenMap is using the value of the pointer to perform this comparison, which is probably not what you want.
I suggest you declare and define PathNode::operator<, and replace mOpenMap with a member of type std::set<PathNode>. This will key off of actual PathNode values, rather than pointers (which will probably never collide under normal circumstances).
Remember that your PathNode::operator< should generate a strict ordering of PathNode objects. This is a requirement of the comparator for std::map and std::set. if you don't follow this rule, it will behave erratically, but it will compile and run, so make sure you pay attention to this detail.
Documentation for std::set

You should check the result as billz said. The most likely reason that the find failed is that your map is keyed on PathNode * meaning that it will only find nodes with an exact pointer match. Searching for a pathnode with the same member values as one in the map will not work.
If you need the map to be on PathNode *, then you will need to also supply a predicate as the third parameter of the map. The predicate will need to be written to compare two PathNode * parameters by their member values.

Related

Why does map have operator[] but set does not?

std::map and std::set seem very similar to me (but in their use and in their description), so I do not understand why std::set does not implement its version of operator[].
I suspect it is linked to the fact that elements in a std::set are const, but even then why not implement an operator[] that either returns a const reference or creates a new element ?
Depending on the answer to this first question, would it be possible/a good idea to create a version a std::set that implements operator[] ?
Well, std::map<Key, Val> maps Key to Val.
That is, m[key] yields a reference to the val. You have the key, and want to find the associated value (or associate a value with that key).
In a std::set<Elem>, the element would be its own key. So, the only thing you could get back would be the thing you already have. What would you use this operation for?
A set is not for mapping one thing to another - that's what a map does. A set is for recording whether or not an element belongs to some collection. So, the only sane thing to use it for is checking, given some element, whether or not that element is a member of the set. We do this with s.find(elem) != s.end() or, from c++20, s.contains(elem).
The fact that the set is described as std::set<Key, ...> may be a source of confusion - I suspect this is just because it's used for searching in the same way as the map key.
You could in principle choose to characterize a set as map<Elem, bool>, but unless you want to really store the bool (which would be wasteful), the element access and iterator semantics would be a bit hairy. That is, it would be mathematically accurate and consistent, but either wasteful or complicated in implementation.
In fact a map is an associated array only instead of integer indices it uses keys as indices.
As ordinary arrays have the subscript operator then and maps have a similar subscript operator.
On the other hand, sets are not associated arrays. In sets keys are their data. So a question arises what should an expression like this set[key] return? There is no greate sense to return itself and moreover when the returned value may not be changed.
It would not be very useful to use set if you want to know the key of an element, since a map would be more useful, which does have the function you require.

C++ set how to sort a set by value and search by key

I am implementing A* shortest path algorithm. The Openlist part stores all the nodes that will be visited. The list is like a priority queue that the first element has the minimum cost value and so in each iteration, we just pop up the first element and visit it. But inside the iteration, we need to loop over the neighbors of this node and check if the neighbor is in Openlist.
So that means this Openlist needs to support two types of operations:
automatic sorting
look up a node(by its ID)
The problem here is that Openlist will sort by the cost value, while look up need to be based on the ID of the neighbor node(the adjacent nodes). So I was considering using set, where the elements are the nodes. But I don't know how to look up an element by its ID in this set.
struct astar_node
{
string id;
double f; //estimated cost;
double g; //distance from source to this node;
double h; //heuristic cost from this node to target;
};
struct openlist_compare
{
bool operator()(const astar_node &node1, const astar_node &node2 ){
return node1.f < node2.f ;
}
};
std::set<astar_node, openlist_compare> Openlist;
C++ containers, like std::set, std::map, std::list, and std::vector, and others, are "single purpose", or "primitive" containers. Each one has certain, unique properties that distinguish them from other containers; otherwise there would be no reason to have all these containers in the first place, of course.
The purpose of a std::set is to look up the values in the container by value, and to store only unique values in the set, that's it.
A std::set, like other associative containers, allow you to specify an optional comparator class that specifies, in effect, what the "value" means, for each actual value in the container. And you have done that, by, in effect, defining that only the f member of your astar_node class, matters for the purposes of defining what the set contains. All other values of all other members of astar_node are ignored.
Once that's done, this is it, as far as the std::set is concerned. The set can be looked up by astar_node::f, and that's it. This is the only way to find something in the set.
As I said in the beginning, std::set, and others, are "primitive" containers, and sometimes you will need something a little bit more sophisticated, as is the case here. In this situation, you can use several containers, and combine them in the manner to achieve the desired result. There is no law against using more than one container in order to store a single set of data. There is no law that says that you must use a single container to do everything that you need to do, with a particular collection of data.
Here, as I understand it, you also need to be able to find an astar_node in the set by the id value.
Fine:
typedef std::set<astar_node, openlist_compare> openlist_t;
openlist_t Openlist;
std::map<std::string, openlist_t::iterator> OpenList_by_id;
After you insert() a new astar_node into your OpenList, insert() returns an iterator for the new element in the openlist_t. You get the iterator for the inserted astar_node. Take this iterator, and put it into OpenList_by_id, with the key being the id.
Now, when you want to find something in OpenList given an id, use the map to find the iterator, then dereference it. Problem solved.
Additionally:
remove()ing an astar_node from the std::set will also require removing its iterator from the OpenList_by_id lookup map.
Since a std::set contains unique values, you need to handle the situation where an astar_node with the same f already exists in the set, and handle this situation appropriately.

How to implement set::find() to match only the key of a pair?

I have a container class which stores data in a std::set. I don't need or use the extended facilities provided by std::map. There is a method values() which returns a const reference to the private set so if I were to use a map instead then I would have to copy the entire container. I want to keep it as a std::set.
The set contains objects of a class similar to std::pair with a key and a value and implements operator < for use in a set.
I have a method in the container which accepts the 'key' portion of the pair for the purpose of searching the set and returning a complete pair while only matching the key.
I can iterate through the set sequentially but then I lose the O(log N).
Also note that the set needs to be sorted, which removes the option of using an unordered_set.
It's not clear exactly what your operator< actually compares, but the long and the short of it is that with a std::set, the only way to efficiently search the set is by using its defined comparison function.
Based on your question, I am assuming that your set is
std::set<std::pair<firstType, secondType>, ComparisonClass>
With ComparisonClass implementing the strict weak ordering. Or, your could also be using a:
std::set<PairClass>
With the PairClass being a subclass of a std::pair, that implements an operator<, for the strict weak ordering. Either one or the other is what appears your question is describing. But either way, both alternatives are logically equivalent, for the purpose of the following answer:
If your operator< implements strict weak ordering based on both the value pair's first and second, then that's pretty much it. You can only execute the set's built-in logarithmic search by searching for the same first and second.
There's no easy way to do anything other than that. So, what now?
Well, the root problem seems to be is that you might not be using the right container. Consider the following container that, with a little bit of work, will be equivalent to your set:
std::multimap<firstType, std::set<secondType>>
That is, your container is a multimap keyed by your pair's first, with the value of your multimap being a std::set of all the secondType that are paired up with a given firstType.
The only thing you have to be careful here is to define insert and remove operation into this container in such a manner, so that you will never end up with a firstType with an empty std::set value. As long as this condition is met, this should be logically equivalent to a std::set of your std::pairs. Furthermore:
1) You can still implement an algorithmic search for a firstType+secondType by, first, a logarithmic search on the firstType, grabbing the value std::set, and then executing a logarithmic search on that. Logically equivalent.
2) You can implement an algorithmic search for just the firstType by doing only the first half of the full search. This gives you the value std::set, that provides the equivalent of all pairs that have the same firstType.

C++ , Displaying a list of existing objects in a multiset ,given specific criteria

Basically I have a class which represents flowers, which has some fields - name, is it flowering (string), family and height.
I need to implement a function that searches for all elements that meet the criteria, which is a specific family name.
I came to the conclusion that a multi-set will be appropriate due to the possible duplicates.
This is my code so far:
searchByFam(string fam,multiset<CFLowers> mySet){
multiset<CFLowers>::iterator it;
for (it = mySet.begin(); it != mySet.end(); ++it){
mySet.find(fam);
}
}
The problem with it is that the dot is getting underlined and with a mouse hover I get the following error : no instance of overloaded function matches the argument type list.
Can someone tell me what is the problem and point me in the right direction.
Thanks
I came to the conclusion that a multi-set will be appropriate due to
the possible duplicates.
Really? The flower class will surely have duplicate family and height values but will the name ever be non unique? The "name" of the flower will most likely be the "key".
If you really do need lookup's and insertions at constant time than an associative container may be your best option, but any variations of a "multi" (set, map, etc...) is completely unnecessary.Just make sure to define your own comparison functions for sorting.
Now the problem with your code is that you haven't defined a comparison operator for your multiset, which is essential for any associative container using a user defined class.
Besides the fact you not defining a comparison operator, there exists a major error here:
for (it = mySet.begin(); it != mySet.end(); ++it){
mySet.find(fam);
}
You are calling find on a multiset which returns an iterator to the location of the first element (or an end iterator). Here, you are never actually using the declared multiset iterator, so you are simply trying to find the first occurrence of a key over and over again. What you are looking for is std::multiset::equal_range which returns two iterators denoting the range of elements specified by the key.

C++ map allocator stores items in a vector?

Here is the problem I would like to solve: in C++, iterators for map, multimap, etc are missing two desirable features: (1) they can't be checked at run-time for validity, and (2) there is no operator< defined on them, which means that they can't be used as keys in another associative container. (I don't care whether the operator< has any relationship to key ordering; I just want there to be some < available at least for iterators to the same map.)
Here is a possible solution to this problem: convince map, multimap, etc to store their key/data pairs in a vector, and then have the iterators be a small struct that contain a pointer to the vector itself and a subscript index. Then two iterators, at least for the same container, could be compared (by comparing their subscript indices), and it would be possible to test at run time whether an iterator is valid.
Is this solution achievable in standard C++? In particular, could I define the 'Allocator' for the map class to actually put the items in a vector, and then define the Allocator::pointer type to be the small struct described in the last paragraph? How is the iterator for a map related to the Allocator::pointer type? Does the Allocator::pointer have to be an actual pointer, or can it be anything that supports a dereference operation?
UPDATE 2013-06-11: I'm not understanding the responses. If the (key,data) pairs are stored in a vector, then it is O(1) to obtain the items given the subscript, only slightly worse than if you had a direct pointer, so there is no change in the asymptotics. Why does a responder say map iterators are "not kept around"? The standard says that iterators remain valid as long as the item to which they refer is not deleted. As for the 'real problem': say I use a multimap for a symbol table (variable name->storage location; it is a multimap rather than map because the variables names in an inner scope may shadow variables with the same name), and say now I need a second data structure keyed by variables. The apparently easiest solution is to use as key for the second map an iterator to the specific instance of the variable's name in the first map, which would work if only iterators had an operator<.
I think not.
If you were somehow able to "convince" map to store its pairs in a vector, you would fundamentally change certain (at least two) guarantees on the map:
insert, erase and find would no longer be logarithmic in complexity.
insert would no longer be able to guarantee the validity of unaffected iterators, as the underlying vector would sometimes need to be reallocated.
Taking a step back though, two things suggest to me that you are trying to "solve" the wrong problem.
First, it is unusual to need to have a vector of iterators.
Second, it is unusual to need to check an iterator for validity, as iterators are not generally kept around.
I wonder what the real problem is that you are trying to solve?