Why is the C++ STL set container's count() method thus named? - c++

What it really checks for is contains() and not the count of the number of occurrences, right? Duplicates are not permitted either so wouldn't contains() be a better name than count()?

It's to make it consistent with other container classes, given that one of the great aspects of polymorphism is to be able to treat different classes with the same API.
It does actually return the count. The fact that the count can only be zero or one for a set does not change that aspect.
It's not fundamentally different to a collection object that only allows two things of each "value" at the same time. In that case, it would return the count of zero, one or two, but it's still a count, the same as with a set.
The relevant part of the standard that requires this is C++11 23.2.4 which talks about the associative containers set, multiset, map and multimap. Table 102 contains the requirements for these associative containers over and above the requirements for "regular" containers, and the bit for count is paraphrased below:
size_type a.count(k) - returns the number of elements with key equivalent to k. Complexity is log(a.size()) + a.count(k).

All associative containers must meet the requirements listed in §23.2.4/8 Table 102 - Associative container requirements. One of these is that they implement a.count(k) which then
returns the number of elements with key equivalent to k
So the reason is to have a consistent interface between all associative containers. For instance, this uniformity will be very important when writing generic function templates that must work with any associative container.

It's a standard operation on containers that returns the number of matching elements. In things like lists, this makes perfect sense. It just so happens that on a set, there can only be one occurrence of an element and therefore count can never return a value greater than 1.

Related

Map count really counts or just checks existence

In CPP primer or other websites I have found the language of count (from map STL) definition very vague and misleading:
Searches the container for elements with a key equivalent to k and returns the number of matches
Now what I have studied so far is that key is singular and so is the mapped value - the mapped value can be changed through assignment.
So doesn't it just returns whether the container contains the key or not? Rather than the count? Where am I wrong in understanding the concept?
A std::map's count() will always return either 0 or 1.
But the C++ library has other associative containers that might very well have multiple values for the same key. Like std::multimap and std::multiset. And by a very lucky coincidence they also have a count() method that may actually return values greater than 1.
But what this allows you to do is metaprogramming by developing templates that can use any associative container, one that may or may not be unique. All your template needs to do is use count() to determine how many values exist in the container with the given key, and the end result can be used with either std::map or std::multimap. It won't care in the slightest. In both cases, your template will get the right answer: the number of values in the container with the given key.
According to cplusplus.com
Because all elements in a map container are unique, the function can only return 1 (if the element is found) or zero (otherwise).

Use of equal_range for unordered_map

As it makes sense, lower_bound and upper_bound are absent for std::unordered_map because there is no order for elements.
However std::unordered_map has method equal_range. it return iterators for range corresponding to a key.
How does it make sense? Since there can be only one element with a key in std::unordered_map. It is just find method.
Also, in std::unordered_multimap, does it presence means all elements with same key will always come together while iterating unordered_multimap with a iterator?
How does it make sense?
It kind of does. The standard requires all associative container to offer equal_range so the non multi containers need to provide it. It does make writing generic code easier so I suspect that is the reason why all of the containers are required to use it.
Does it presence means all elements with same key will always come together while iterating unordered_map with a iterator?
Actually, yes. Since the all of the keys will have the same value they will hash to the same value which means they all get stored in the same bucket and will be grouped together since the keys compare equally. From [unord.req]/6
An unordered associative container supports unique keys if it may contain at most one element for each key. Otherwise, it supports equivalent keys. unordered_­set and unordered_­map support unique keys. unordered_­multiset and unordered_­multimap support equivalent keys. In containers that support equivalent keys, elements with equivalent keys are adjacent to each other in the iteration order of the container. Thus, although the absolute order of elements in an unordered container is not specified, its elements are grouped into equivalent-key groups such that all elements of each group have equivalent keys. Mutating operations on unordered containers shall preserve the relative order of elements within each equivalent-key group unless otherwise specified.
emphasis mine
It's for consistency with the other containers.
It makes more sense in the _multi variants, but is present in all the associative (and unordered associative) containers in the standard library.
It allows you to write code like
template <typename MapLike, typename KeyLike>
void do_stuff(const MapLike & map, const KeyLike & key)
{
auto range = map.equal_range(key);
for (auto it = range.first; it != range.second; ++it)
// blah
}
Which does not care about what specific container it is dealing with
cplusplus.com writes about std::unordered_map::equal_range:
Returns the bounds of a range that includes all the elements in the container with a key that compares equal to k. In unordered_map containers, where keys are unique, the range will include one element at most.
Also, in std::unordered_multimap, does it presence means all elements with same key will always come together while iterating unordered_multimap with a iterator?
In general, the order, in which elements stored in a std::unordered_multimap are obtained while traversing it, is actually not defined. However, note that std::unordered_multimaps are usually implemented as hash tables. By analysing such an implementation you will realize that the ordering is not going to be as "undefined" as someone might initially think.
At element insertion (or hash table rehashing), the value resulting of applying the hash function to an element's key is used to select the bucket where that element is going to be stored. Two elements with equal keys will result in the same hash value, therefore they will be stored in the same bucket, so they come togetherX while iterating an std::unordered_multimap.
XNote that even two elements with different keys might also result in the same hash value (i.e., a collision). However, std::unordered_multimap can handle these cases by comparing the keys against equality, and therefore still group elements with equal keys together.

What is std::set::equal_range for?

Since std::set cannot contain duplicate elements and is always sorted, std::set::equal_range will always return the range that has either none or 1 element. Technically, yes, that's still a range, but what is the purpose of this algorithm? For std::set it seems pretty unnecessary.
I'm only guessing. But, like count(), it has some value when you're in a template and don't want to have to determine whether you're operating on a std::set or some other associative container.
Basically, it's for consistency. The function does perform as advertised, it's just that yes it has questionable use versus something like find() if you take it in isolation. It does potentially save you one manual iterator increment if you really do want a half-open range out of the box though. :P
All of the associative containers support equal_range, which means you can write generic code which accepts a set, multiset, map or multimap and it will do the right thing.
Consider std::multiset. Despite practically identical in interface, it can contain equal_ranges of higher lengths.

loop a std::unordered_map, the sequence is always the sequence I insert elements?

I constructed a std::unordered_map and use for loop to visit it.
I found that the sequence of the iteration result shows the elements is put in the sequence that I created those elements, no matter how I inserted them.
Is this part of C++ standard that unordered_map, when visited, the iteration sequence is the insertion sequence? Or this is implementation preference?
I ask this question is, I wish to know if this feature, is something I can rely in my c++ code?
No. The standard makes no guarantees about the order of elements in the unordered associative containers † (unordered map, set and their multivalued versions) and you can not rely on any particular ordering in your code.
† Except for a special case [unord.req]/6 (standard draft, emphasis mine):
An unordered associative container supports unique keys if it may contain at most one element for each key. Otherwise, it supports equivalent keys. unordered_set and unordered_map support unique keys. unordered_multiset and unordered_multimap support equivalent keys. In containers that support equivalent keys, elements with equivalent keys are adjacent to each other in the iteration order of the container. Thus, although the absolute order of elements in an unordered container is not specified, its elements are grouped into equivalent-key groups such that all elements of each group have equivalent keys. Mutating operations on unordered containers shall preserve the relative order of elements within each equivalent-key group unless otherwise specified.

Does the C++11 standard require that two iterations through a constant unordered_container visit elements in the same order?

for (auto&& i : unordered_container)
{ /* ... */ }
for (auto&& i : unordered_container)
{ /* .. */ }
Does the standard require that both of these loops visit elements in the same order (assuming the container is unmodified)?
My analysis of this question...
I read the standard and as best I can tell the answer is "no"...
Since iterators of containers are forward, there is language that requires a==b imply that ++a==++b for forward iterators. That means two iterations will go through the same path IF they both start in the same place. This reduces the question to a different question of whether the standard requires container.begin() == container.begin(). I couldn't find any language that requires this.
Containers are required to implement operator==(). That is we can do:
container c;
c == c;
That relation is required to work the same as:
std::distance(a.begin(), a.end()) == std::distance(b.begin(), b.end()) &&
std::equal(a.begin(), a.end(), b.begin());
The important part here is the call to std::equal(). This call requires that two independent calls to container.begin() will produce the same sequence of values. If it didn't, then c == c would be false, and that doesn't make any sense because == is an equivalence relation.
Therefore, my answer is that we can claim that the standard requires that two passes of any container must result in the same ordering. Obviously this requirement breaks if you do anything that changes the container or invalidates iterators.
Citations:
C++ 2011 Table 96 — Container requirements
I think #Sharth's conclusion is correct, but (for anybody who cares about newer standards) is already obsolete (and may not have ever reflected reality--see below).
More recent drafts of the standard (e.g., n3797) have changed the requirements, apparently intentionally removing the ordering requirement. Specifically, it says (§23.2.5/12):
Two unordered containers a and b compare equal if a.size() == b.size() and, for every equivalent-key group [Ea1,Ea2) obtained from a.equal_range(Ea1), there exists an equivalent-key group [Eb1,Eb2) obtained from b.equal_range(Ea1), such that distance(Ea1, Ea2) == distance(Eb1, Eb2) and is_permutation(Ea1, Ea2, Eb1) returns true.
I also have relatively low confidence that implementations actually meet the requirements of the 2011 standard either. In particular, the unordered containers are normally implemented as hash tables with linked lists for collision resolution. Since those linked lists are expected to be short, they're not necessarily sorted (particularly since items stored in unordered containers aren't required to define operations to be used for sorting, such as operator<). This being the case, it's fairly routine for the linked lists to hold the same items, but in an order that depends upon the order in which they were inserted.
In such a case, it would be fairly routine for two hash tables that contained the same items that had been inserted in different orders to iterate over those items in different orders.
In theory such an implementation doesn't conform with the C++11 standard--but I'd guess the change cited above was made largely because that requirement couldn't be met in practice (because, as noted above, the container had no way to enforce ordering).
So, as long as you're dealing with the same container, unchanged, depending on iteration in the same order may be safe. Two containers that have the same contents may not work out so well though (and even in what claims to be a C++11 implementation, you probably can't expect it to meet tighter requirements than the newer drafts contain).
My read of the standard is that this is not guaranteed. 23.2.5, paragraph 6, states:
Thus, although the absolute order of elements in an unordered
container is not specified, its elements are grouped into
equivalent-key groups such that all elements of each group have
equivalent keys. Mutating operations on unordered containers shall
preserve the relative order of elements within each equivalent-key
group unless otherwise specified.
Let's take off the table the fairly clear guarantee that elements that hash to the same key will have their relative order preserved no matter what. That seems to be fairly clear. Additionally, lets exclude any modifications to the container. In the remaining scope:
Although this doesn't actually explicitly define that the iteration order, in absence of changes to the container, is unstable, I interpret the statement "the absolute order of elements in an unordered container is not specified" on its literal face value. If the iteration order is undefined, then it is undefined, and is not guaranteed to be the same every time.
I think it all comes down to whether, in the quoted excerpt "is not specified" should be interpreted as "it could be anything" or "it could be anything, at any given time".
I think an argument can be made either way. I would interpret "is not specified" in the most strict, literal interpretation of the latter, but I wouldn't object too hard if someone would argue in favor of the former.
Unordered containers do return forward iterators (which are defined in § 24.2.5) and those do have this property: a == b implies ++a == ++b. This seems to imply so long that unordered_container.begin() == unordered_container.begin() is true, that the traversal order will be the same.
I was unable to find any language that requires unordered_container.begin() == unordered_container.begin() which led me to a tentative answer of "no", the traversal order isn't required to be the same.