Why does map have operator[] but set does not? - c++

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.

Related

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.

How can I find out if a map contains a given value?

I'd like to find out whether a given value is present in a map. Also getting the corresponding key(s) would be nice but is not required.
bool map::contains(string value);
Is there a simple way to do this other than to iterate over the whole map and comparing each value with the given value? Why is there no corresponding method in the STL?
std::map only indexes its elements by key; it does not index them by value. Therefore, there is no way to look up an element by its value without iterating over the map.
Take a look at Boost.Bimap:
Boost.Bimap is a bidirectional maps library for C++. With Boost.Bimap you can create associative containers in which both types can be used as key. A bimap<X,Y> can be thought of as a combination of a std::map<X,Y> and a std::map<Y,X>.
Using it is pretty straightforward, although you will of course need to consider the question of whether duplicate values are allowed.
Also, see Is there a Boost.Bimap alternative in c++11?

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

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.

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?

Convert object reference to STL iterator

I am using std::multimap<> and I pass pointer to an element (T*) to a component written in C.
When the component wants to delete the object it calls back to C++ supplying the pointer, however, I am not sure whether there is a way to convert T* into std::multimap<>::iterator so that I can call erase().
Any ideas?
If you can determine the key from the item, you can use equal_range to get all the possible matches, then call find on that range.
If there isn't a way to get from an item to it's key (rare but possible), then one could enumerate through the whole multimap (from begin() to end()) and erase the one that matches. Hopefully this would be a rare operation, as it is O(N).
Do not confuse pointers and iterators. Sometimes (e.g. arrays) a pointer can function as iterator. But it doesn't necessarily.
Iterators in C++ will usually overload the * operator aka "dereference operator". This makes them look like C pointers even more, when they technically may or may not be the same.
Passing iterators is in general fragile, and I'd avoid this. In particular, a concurrent modification of the multimap in your case may render the iterator invalid.
Remember that a multimap is a set of key-value pairs. It sounds like your T* is a value and you need an efficient way to get its key so you can remove it. Have you considered Boost.Bimap? That library allows efficient mappings both ways. Then it should be simple to take the T* from the calling code, lookup the key, and erase it.