So the problem is the following, I have a multiset where I use the std::equal_to operator for comparing the elements, but when I use the count() method it says all 4 elements in my multiset are equal_to my counts parameter.
std::multiset< std::string, std::equal_to< std::string > > mset;
mset.insert("C++");
mset.insert("SQL");
mset.insert("Jav");
mset.insert("C");
for(std::multiset<std::string>::iterator it = mset.begin(); it != mset.end(); ++it){
std::cout << *it << std::endl;
}
std::cout << std::endl;
std::cout << mset.count("STR");
The output is : 4
If i understand right whats happening is "STR"=="C++"=="SQL"=="Jav"=="C"==true.
And this is what I don't understand.
Thankyou for the help.
As BobTFish already said in a comment, the Compare type of std::multiset should return true if the first argument is "less" (has to be ordered before) the second argument. The default type is std::less<Key>.
For elements stored in std::multiset you must define strict weak ordering relation f(x, y). One of the properties of strict weak ordering is Irreflexivity, that is f(x, x) must be false. This property is violated in your strict weak ordering and you have got some undefined results.
What you probably want is to use std::unordered_multiset instead.
Related
According to http://www.cplusplus.com/reference/map/multimap/emplace_hint/
The relative ordering of equivalent elements is preserved, and newly inserted elements follow their equivalents already in the container.
The value in position is used as a hint on the insertion point. The element will nevertheless be inserted at its corresponding position following the order described by its internal comparison object, but this hint is used by the function to begin its search for the insertion point, speeding up the process considerably when the actual insertion point is either position or close to it.
As I understand, the hint is just a hint and should not affect the ordering at all.
But it seems that it is not the case for both clang++(11) and g++(10), no matter the options or c++ the standard specified.
int main()
{
std::multimap<int, string> mymap;
// emplace
mymap.emplace(0, "First");
mymap.emplace(0, "Second");
// emplace_hint
mymap.emplace_hint(mymap.end(), 1, "First");
// Insert with the previously inserted element as hint
mymap.emplace_hint(--mymap.end(), 1, "Second");
for (auto it = mymap.begin(); it != mymap.end(); ++it) {
std::cout << it->first << " " << it->second << std::endl;
}
return 0;
}
Outputs
0 First
0 Second
1 Second
1 First
Is this the expected behavior?
That cite, from cplusplus.com, appears to be in error. From the C++ standard:
22.2.6 Associative containers [associative.reqmts]
...
An associative container supports unique keys if it may contain at
most one element for each key. Otherwise, it supports equivalent keys.
The set and map classes support unique keys; the multiset and multimap
classes support equivalent keys. For multiset and multimap, insert,
emplace, and erase preserve the relative ordering of equivalent
elements.
Note that only emplace is listed, but not emplace_hint.
The same identical wording appears in the C++11 standard, as well as the current standard, so this wording has not been changed any time recently.
I'm new to C++ and I've been experimenting with the language lately.
I started doing some basic iterations with map.
What I found was that the following code:
map<string, int> persons = {{"Lily", 14}, {"John", 45}};
for ( const auto &p : persons ) {
cout << p.first << " is " << p.second << " years old." << endl;
}
Always returns:
John is 45 years old.
Lily is 14 years old.
No matter what the order of persons is (eg if I switched up Lily & John).
Is there any ordering within map?
Yes.
std::map (as well as std::set) is ordered according to its Comparator, which defaults to std::less which calls the overload of operator < for the stored keys.
Hence, std::strings are ordered lexicographically.
Yes, there is ordering in map.
To be specific, an std::map orders items by the keys. In this case, you've use std::string as the key, so the keys are ordered by comparing strings. Since J comes before L in the alphabet, it's ordered first in the map as well.
If you prefer, you can supply your own comparison routine (as a function, or preferably, a function object) that specifies a different ordering (but it still has to satisfy a "strict weak ordering" criteria, so (for example) A<B and B<C implies that A<C).
What is the difference between set::key_comp vs set::value_comp in C++? Going to cplusplus.com page there is no significant difference.
Furthermore on set::key_comp & related set::value_comp pages
last sentence is "(...) key_comp and its sibling member function value_comp are equivalent."
Examples are almost the same:
http://www.cplusplus.com/reference/set/set/key_comp/
http://www.cplusplus.com/reference/set/set/value_comp/
key_comp defines the order of the keys in a container.
value_comp defines the order of the values in a container.
In a std::set where, essentially, the values are the keys, the two are indeed exactly equivalent. But that's not true in all containers, e.g. std::map, or, in general, in a container that you might build yourself that follows the conventions of the C++ Standard Library Containers.
Note also that http://en.cppreference.com/w/ is a superior reference for C++. It pretty much proxies the standards.
These are identical, both must be made available by any implementation because std::set must meet the requirement of Associative Container.
This allows you to write generic code that works with any Associative Container (std::set, std::map, std::multiset, std::multimap in the standard library).
The difference comes when key and value are different entities inside a container.
For containers like set, these two terms mean same thing.
While, for containers like map or multimap, the key and value are separate entities maintained as an single entry.
Here is an example which shows how they differ:
std::set<int> myset;
int highest1, highest2, highest3;
typedef map<int, int> MyMap;
MyMap mymap;
std::set<int>::key_compare myCompKeyForSet = myset.key_comp();
std::set<int>::value_compare myCompValForSet = myset.value_comp();
MyMap::key_compare myCompKeyForMap = mymap.key_comp();
MyMap::value_compare myCompValForMap = mymap.value_comp();
for (int i=0; i<=5; i++) {
myset.insert(i);
mymap.insert(make_pair(i, 2*i));
}
//////SET///////
highest1=*myset.rbegin();
std::set<int>::iterator it=myset.begin();
while ( myCompKeyForSet(*it, highest1) ) it++;
std::cout << "\nhighest1 is " << highest1; // prints 5
highest2 = *myset.rbegin();
it=myset.begin();
while ( myCompValForSet(*it, highest2) ) it++;
std::cout << "\nhighest2 is " << highest2; // prints 5
//////MAP///////
MyMap::iterator it2 = mymap.begin();
highest3 = mymap.rbegin()->first;
while ( myCompKeyForMap((it2->first), highest3) ) it2++;
std::cout << "\nhighest3 is " << highest3; // prints 5
std::pair<int,int> highest4 = *mymap.rbegin(); //must be defined as map's `value_type`
it2 = mymap.begin();
while ( myCompValForMap(*(it2), highest4) ) it2++; // takes `value_type` which is `pair<int, int>` in this case.
std::cout << "\nhighest4 is " << highest4.second; // prints 10
Live demo
As I mentioned the passed arguments to value_compare function object must be of type value_type&, so I am in a kind of disagreement with those saying that these two set::key_comp and set::value_comp are easily compatible across associative containers.
Is there any reason for std::vector's operator[] to just return a reference instead of inserting a new element? The cppreference.com page for vector::operator says here
Unlike std::map::operator[], this operator never inserts a new element into the container.
While the page for map::operator[] says
"Returns a reference to the value that is mapped to a key equivalent to key, performing an insertion if such key does not already exist."
Why couldn't vector::operator[] be implemented by calling vector::push_back or vector::insert like how map::operator[] calls insert(std::make_pair(key, T())).first->second;?
Quite simply: Because it doesn't make sense. What do you expect
std::vector<int> a = {1, 2, 3};
a[10] = 4;
to do? Create a fourth element even though you specified index 10? Create elements 3 through to 10 and return a reference to the last one? Neither would be particularily intuitive.
If you really want to fill a vector with values using operator[] instead of push_back, you can call resize on the vector to create the elements before settings them.
Edit: Or, if you actually want to have an associative container, where the index is important apart from ordering, std::map<int, YourData> might actually make more sense.
A map and a vector are completely different concepts. A map is an "associative container" whereas a vector is a "sequence container". Delineating the differences is out of the scope of this answer, though at the most superficial of levels, a map is generally implemented as a red-black tree, while a vector is a convoluted wrapper over a C-style array (elements stored contiguously in memory).
If you want to check if an element already exists, you would need to resize the entire container. But what happens if you decide to remove the element? What do you do with the entries you just created? With a map:
std::map<int, int> m;
m[1] = 1;
m.erase(m.begin());
This is a constant operation.
With a vector:
std::vector<int> v;
// ... initialize some values between 25 and 100
v[100] = 1;
v.erase(v.begin() + 25, v.end());
This is a linear operation. That's horribly inefficient (comparatively) to a map. While this is a contrived example, it's not hard to imagine how this could blow up in other scenarios. At a minimum, most people would go out of their way to avoid operator[] which as a cost in of itself (maintenance and code complexity).
Is there any reason for std::vector's operator[] to just return a reference instead of inserting a new element?
std::vector::operator[] is implemented in an array-like fashion because std::vector is a sequence container (i.e., array-like). Standard arrays for integral types cannot be accessed out of bounds. Similarly, accessing std::vector::operator[] with an index outside of the vector's length is not allowed either. So, yes, the reasons it is not implemented as you ask about is because in no other context, do arrays in C++ act like that.
std::map::operator[] is not a sequence container. Its syntax makes it similar to associative arrays in other languages. In terms of C++ (and its predecessor, C), map::operator[] is just syntactic sugar. It is the "black sheep" of the operator[] family, not std::vector::operator[].
The interesting part of the C++ specification regarding is that accessing a map with a key that doesn't exist, using std::map::operator[], adds an element to the map. Thus,
#include <iostream>
#include <map>
int main(void) {
std::map<char, int> m;
m['a'] = 1;
std::cout << "m['a'] == " << m['a'] << ", m.size() == " << m.size() << std::endl;
std::cout << "m['b'] == " << m['b'] << ", m.size() == " << m.size() << std::endl;
}
results in:
m['a'] == 1, m.size() == 1
m['b'] == 0, m.size() == 2
See also: Difference between map[] and map.at in C++? :
[map::at] throws an exception if the key doesn't exist, find returns aMap.end() if the element doesn't exist, and operator[] value-initializes a new value for the corresponding key if no value exists there.
#include <iostream>
#include <set>
#include <tuple>
struct Key {
int field1;
int field2;
Key(int field1, int field2) : field1(field1), field2(field2) {}
bool operator<(const Key& other) const {
// Is this acceptable?! Seems to work
if (field2 == 0 || other.field2 == 0) {
return field1 < other.field1;
} else {
return std::tie(field1, field2) < std::tie(other.field1, other.field2);
}
}
};
int main() {
std::set<Key> values{Key(4,3), Key(5,9), Key(5,7), Key(5,8), Key(6,1)};
std::cout << values.find(Key(5,0))->field2 << std::endl; // Prints '7'
auto range = values.equal_range(Key(5,0));
for (auto i = range.first; i != range.second; i++) {
std::cout << i->field2; // Prints '789'
}
return 0;
}
Field2 is not always available in my data, so sometimes I use a wildcard value of 0, which can match any value for which field1 matches. Is this valid in C++ if I never insert elements that have a wildcard value, and only ever look them up in the set? I'm okay with the find function returning any of the values in this case which happens rarely in my code, though hopefully it would be the same value when called repeatedly.
According to the specification, it seems like strict weak ordering is not required for binary_search, which should be the only algorithm used on the data structure when performing a lookup, right? Or is there some undefined behavior I should worry about here?
25.4 Sorting and related operations
... For algorithms other than those described in 25.4.3 to work
correctly, comp has to induce a strict weak ordering on the values...
25.4.3 Binary search
You're mistaken. std::set::find does a lookup in a binary search tree (in a typical implementation). That might seem like binary search algorithm, but the algorithms in 25.4.3 are not typically used for the lookup. A tree supports only non-random-access iterators and binary search with linear iterators is much slower than a lookup using the knowledge that the data is in a BST.
The comparator of std::set must comply to the Compare concept, which does require strict weak ordering.
Is this valid in C++ if I never insert elements that have a wildcard value, and only ever look them up in the set?
Technically no, since you're breaking the requirements. At the very least you will have indeterminate results, when looking up {x, 0} from a set that contains {x, a} and {x, b}. Either could be found. If that doesn't matter, then I doubt a typical implementation would pose trouble. What you're doing is not guaranteed to work by the standard though, which is enough for most people to shy away from it.