set::key_comp vs set::value_comp in C++? - 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.

Related

Iterators and range based for loops

I'm trying to understand iterators and different ways to iterate over elements in different containers. What's the difference between these two options?
for(auto it = myArray.begin(); it < myArray.end(); it++){
std::cout << *it << std::endl;
}
for(auto it = myArray.begin(); it != myArray.end(); it++){
std::cout << *it << std::endl;
}
When using the second option my editor suggests using a range based for loop in stead, which made me realise I don't know the difference.
for(int & it : myArray){
std::cout << it << std::endl;
}
What's the difference between these two options?
Not all types of input iterators are comparable using the < operator (only random-access iterators are, but not all iterators are random-access). All input iterators are comparable using the == and/or != operator, though.
When using the second option my editor suggests using a range based for loop instead
As you should. A range-for loop uses iterators internally, similar to your second for loop. The loop variable is the dereferenced value, not the iterator itself. A range-for loop does not expose access to its internal iterators.

Does emplace_hint on std::multimap preserves the relative ordering of equivalent elements?

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.

Order of map in C++?

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).

C++ arrays [from:to]

How can i do that in C++?
in python is
example = [u'one', u'two', u'three', u'four']
print example[1:3]
How can i do that in C++ (i missing this function)
I need rewrite this to C++
while i<len(a)-1:
if (a[i]=='\x00' or a[i]=='\x04') and (eval("0x"+(a[i-1].encode("hex"))) in range(32-(4*eval((a[i].encode("hex")))),128-(12*eval((a[i].encode("hex")))))):
st+=a[i-1:i+1]
i+=2;continue
elif st=='':
i+=1;continue
elif len(st)>=4 and (a[i-1:i+1]=='\x00\x00' or a[i-1:i+1]=='\x0a\x00' or a[i-1:i+1]=='\x09\x00' or a[i-1:i+1]=='\x0d\x00'):
s.STRINGS.append([st.decode("utf-16le"),0xffffff])
s.INDEX.append(iCodeOffset+i-1-len(st))
st=''
i=i-1;continue
else:
st=''
i=i-1;continue
I need list of strings from binary files without using string.exe
THX for advance
Benecore
Here is a function that returns a new spliced vector given then old one. It does only the most basic splicing (from:to), and only in one direction (not sure if from is greater than to but I believe python reverses the output).
template<typename T>
std::vector<T> splice(const std::vector<T> in, int from, int to)
{
if (to < from) std::swap(to, from);
std::vector<T> ret(to - from + 1);
for (to -= from; to + 1; to--)
{
ret[to] = in[from + to];
}
return ret;
}
First of all, there is no immediate replacement for this in C++, as C++ is not python and has its own idioms that work differently.
To begin with, for strings you can use the specific std::string::substr.
For more generic containers you should know C++ usually works iterator based when operating on elements of said container. For example suppose you want to compare elements in a vector, you'd do something like the following:
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> a = {1,2,3,4};
std::vector<int> b = {1,2,10,4};
std::cout << "Whole vectors equal? " << (std::equal(a.begin(), a.end(), b.begin())?"yes":"no") << std::endl;
}
Now, suppose we only want to compare the first two values (like [:2]), Then we would rewrite the last statement to something like this:
std::cout << "First 2 values equal? " << (std::equal(a.begin(), a.begin()+2, b.begin())?"yes":"no") << std::endl;
Suppose we want to compare the last two values we would do this:
std::cout << "Last 2 values equal? " << (std::equal(a.end()-2, a.end(), b.begin())?"yes":"no") << std::endl;
See the pattern emerging? x.begin()+i,x.begin()+j is roughly equal to [i:j], and x.end()-i,x.end()-j) is roughly equal to [-i,-j]. Note that you can mix these of course.
So in general when working on containers you will work on a range of iterators and this iterator range can be specified very much alike to python's list splicing. It is more verbose and it is another idiom (spliced lists are lists again but iterators are no containers), but you get the same result.
Some final notes:
I wrote x.begin() to make the code a bit clearer, you can also write std::begin(x), which is more generic and also works on arrays. The same goes for std::end
Take a look to the algorithms library before writing your own for loops over iterators.
Yes you can write your own for loops (something like for(auto it = a.begin(); it != a.end(); it++), but often it's easier and more consistent to pass a function or lambda to std::foreach
Really remember C++ is not python or vice versa.

c++ container search by key and value

I'm trying to build a container of string representations of ordinal numbers, searchable both by the string and by the number. For example, I can do it trivially, but inefficiently, with an array:
std::string ordinalStrings = {"zeroth", "first", "second",..}
getting the string with ordinalStrings[number], and the integer with a while(not found) loop, but I figure that one of the STL containers would do a better job, just not sure which one.
It needn't be searchable by both, for example, simply getting the numerical key of a map with a string key would work, but there doesn't seem to be a function for it, and besides, it seems a little messy.
As an aside, is it possible to have a constant STL container? I only need the searching functionality, not the insertion. Also, since I assume I can't, is it possible to fill the container without an initializer function? That is, can a header simply say:
std::map<std::string, int> ordinals;
ordinals["zero"] = 0;
ordinals["one"] = 1;
...
It just seems silly to have an initializer function for what is essentially a constant value;
Thanks,
Wyatt
Stephen suggested using Boost.MultiIndex, however it's a bit an overkill.
Boost.Bimap has been developped over it, and offer extra functionalities. It is in fact tailored right for this task. It's also one of the few libraries which has (imho) a good documentation :)
Here is an example right from this page.
#include <iostream>
#include <boost/bimap.hpp>
struct country {};
struct place {};
int main()
{
using namespace boost::bimaps;
// Soccer World cup.
typedef bimap
<
tagged< std::string, country >,
tagged< int , place >
> results_bimap;
typedef results_bimap::value_type position;
results_bimap results;
results.insert( position("Argentina" ,1) );
results.insert( position("Spain" ,2) );
results.insert( position("Germany" ,3) );
results.insert( position("France" ,4) );
std::cout << "Countries names ordered by their final position:"
<< std::endl;
for( results_bimap::map_by<place>::const_iterator
i = results.by<place>().begin(),
iend = results.by<place>().end() ;
i != iend; ++i )
{
std::cout << i->get<place >() << ") "
<< i->get<country>() << std::endl;
}
std::cout << std::endl
<< "Countries names ordered alphabetically along with"
"their final position:"
<< std::endl;
for( results_bimap::map_by<country>::const_iterator
i = results.by<country>().begin(),
iend = results.by<country>().end() ;
i != iend; ++i )
{
std::cout << i->get<country>() << " ends "
<< i->get<place >() << "ยบ"
<< std::endl;
}
return 0;
}
You can provide two different lookup mechanisms using boost::multi_index (here's an example of a bidirectional map using multi_index). Another (maybe simpler) option is to maintain two containers: one to lookup by ordinal, one to search by string. You could use two std::map or a std::map and a std::vector (for constant time lookup of ordinality).
As an aside, is it possible to have a constant STL container? I only need the searching functionality, not the insertion.
Yes, but you must initialize a non-const container. You can then copy that into a const container. For example, you can use a helper function: const std::map<string, int> ordinals = create_map();
That is, can a header simply say: ...
No, you should initialize the container within a proper control flow.