Requirement for key in std::multimap - c++

I have a std::multimap where key is a custom class. Something like this:
Class X {
public:
std::string s;
int x;
operator <(const X& other) const { return s < other.s; }
};
std::multimap<X, int> mymap;
Now, I'd like to use upper_bound and lower_bound to iterate over all elements with the same value of "s". Do I need to implement some other operator for X (for example: ==). Or it will work properly just like this?
Also, what should I supply as argument for upper_bound and lower_bound? I assume I should create a dummy object with desired value of "s"?

Since class X is the key for the multimap, the parameter to upper_bound()/lower_bound() needs to be of that type. If class X has an implicit conversion from std::string (which is the type of X::s) then you can use that as the parameter to upper_bound()/lower_bound().
The default comparison for multimap is less<> which simply calls operator <() - so that's the only operator you a required to have in class X for the multimap to work.

you only need to provide an operator == and <.
upper_bound and lower_bound are just like any other find-type method, so you need the same kind of object to compare with - in your case, a 'dummy' object with the required value of s.
edit: the comments are correct that you only need operator< for lower/upper_bound, and find. But if you want to call other methods on your container, you will need operator== as well. Eg. if you want to sort() your container, you will need operator==.
The 2 overloads you need for all STL containers are operator< and operator==. I find its best practise to implement them both.
Of course, the question could also be answered more fully by implementing a comparison functor in the map itself, not relying on the objects. This is often a good way to implement different ways of calling find() on the map.

Related

Why do I have to implement operator< when using operator() for sorting a std::set

During a code review a colleage from mine was sorting a std::set using a struct. I am still quite new in C++ and had to implement it myself to fully understand it. Sadly I had some struggle because the MSVC forced me to implement operator< too after I implemented operator() of the struct.
Can someone explain me why it is necessary to implement both operator if I use a struct for sorting a std::set? I guessed that operator< is not needed because std::set calls some basic compare function?
class Hallo {
int one;
int two;
public:
Hallo(int one, int two);
bool operator < (const Hallo& rhs) const
{
return one < rhs.GetOne();
}
struct cmpStruct{
bool operator()(Hallo const &lhs, Hallo const &rhs) const
{
return lhs.GetOne() < rhs.GetOne();
}
int main(int ac, char* av[]){
const Hallo a{ 1, 1 };
const Hallo b{ 2, 2 };
const Hallo c{ 3, 3 };
const Hallo d{ 5, 5 };
std::set<Hallo, Hallo::cmpStruct> sortedList{};
std::set<Hallo> unsortedList{};
sortedList.insert(b);
sortedList.insert(c);
sortedList.insert(a);
sortedList.insert(d);
unsortedList.insert(b);
unsortedList.insert(c);
unsortedList.insert(a);
unsortedList.insert(d);
Can someone explain me why it is necessary to implement both operator if I use a struct for sorting a std::set?
Because you created 2 instances of std::set with class Hallo as a key and in the first you explicitly used cmpStruct as a functor, but in the second you implicitly use std::less as stated in documentation
template<
class Key,
class Compare = std::less<Key>,
class Allocator = std::allocator<Key>
> class set;
and std::less uses "some basic compare function" which is operator< documentation:
Function object for performing comparisons. Unless specialized, invokes operator< on type T.
emphasis is mine. So unless you specialize std::less for class Hallo or would not replace std::less in std::set instantiation with something else it would require operator< either as method or standalone function.
By default, std::set uses operator < to compare two Hallo instances. This is why you would need to define an operator< function for comparing them. There is no default operation for comparing structs.
Edit 1: Specifying an ordering or comparison function
One of the std::set constructors allows you to specify a function for comparing Hallo instances. If you don't want to add an overloaded operator< method to your struct, you'll need to pass a function that compares two Hallo instances, to the std::set constructor. Again, there are no default comparison operators for struct or class instances; you'll have to create something.
Edit 2: Purpose of comparison function
The comparison function argument of std::set allows you specify a comparison function for your Hallo structure. It allows you to create one set that orders by member one and you could create another set that orders by the two member.
Also, you can define the ordering function for ascending or descending orders. The choices are plentify.
The members of any mathematical set are unique; no two can have the same key. But what does the same mean for a user-defined type like struct? We know what it means for two strings to be equal, or two integers. But the meaning of "same" for your struct is up to you, and up to you to define.
The members of std::set are both unique and sorted, by definition. So, beyond enforcing uniqueness, std::set represents the members in order. What order? Again, for user-defined types, it's up to the user to define what it means for one object to be "less than" another.
That's where operator< comes in. You define what it means for one of your objects to be less than the other. std::set calls your user-defined operator on your user-defined type, and puts the members in the order thus defined. It also uses that function to enforce uniqueness: If a new item cannot be inserted before an existing one nor after it, it's equal to it, and the insertion is rejected.

What is the use of operator<?

In the code snippet below, please, if someone can clarify what is the function of bool operator<... and why it is used as a function?
bool operator<(const RankedFace& other) const
{
if (lastDelay == other.lastDelay)
return face.getId() < other.face.getId();
return lastDelay < other.lastDelay;
}
It's the (in class) definition of the operator< for a user defined type (RankedFace I guess).
Thanks to that code, you will be able to compare two objects of type RankedFace with the <, e.g. if( r1 < r2 ) // do something...
It gives the type RankedFace a less-than comparison (operator<). As declared; it looks like a member method. It could also have been a non-member method with the following signature;
bool operator<(const RankedFace& lys, const RankedFace& rhs)
It is typically required for use in the standard library associative containers (std::set etc.).
The associative containers require a comparator to order the objects in them. A custom comparator can be used, but the standard one is std::less which is simply a lhs < rhs.
It allows client code to use the less than comparison on objects of that type (face1 < face2). It is often (not always) implemented together with other comparators (==, !=, <= etc.). If operator< and operator== have been implemented, the remaining ones can be implemented using std::rel_ops.
This is RankedFace's less than operator. It compares two RankedFace objects. For example:
RankedFace foo;
RankedFace bar;
cout << foo < bar ? "foo is smaller than bar" : "bar is greater than or equal to foo";
In the code above foo < bar causes C++ to call foo.operator<(bar).
Dissection of RankedFace::operator< reveals that it:
Considers the object with the lower lastDelay member the smaller object
For objects with identical lastDelays it considers the object which returns a lower getId() the smaller object.
An actual in code comparison between RankedFaces may not exist. The motivation for implementing the less than operator may have been that the less than operator is required to use a RankedFace as in the key in any associative container or unordered associative container.

Does QMap support custom comparator functions?

I couldn't find a way to set a custom comparator function for QMap, like I can for std::map (the typename _Compare = std::less<_Key> part of its template arguments).
Does QMap have a way to set one?
It's not documented (and it's a mistake, I think), but in you can specialize the qMapLessThanKey template function for your types (cf. the source). That will allow your type to use some other function rather than operator<:
template<> bool qMapLessThanKey<int>(const int &key1, const int &key2)
{
return key1 > key2; // sort by operator> !
}
Nonetheless, std::map has the advantage that you can specify a different comparator per each map, while here you can't (all maps using your type must see that specialization, or everything will fall apart).
No, as far as i know QMap doesn't have that functionality it requires that it's key type to have operator<, so you are stuck with std::map if you really need that compare functionality.
QMap's key type must provide operator<(). QMap uses it to keep its items sorted, and assumes that two keys x and y are equal if neither x < y nor y < x is true.
In case, overload operator<().

What is `type_info::before` useful for?

According to cplusplus.com, the std::type_info::before() function...
Returns true if the type precedes the type of rhs in the collation order.
The collation order is just an internal order kept by a particular implementation and is not necessarily related to inheritance relations or declaring order.
So what is it useful for?
Consider you want to put your type_info objects as keys into a map<type_info*, value>. The type_info doesn't have an operator < defined, so you must provide your own comparator. The only thing that is guaranteed to work from the type_info interface is the before() function, since neither the addresses of type_info nor the name() must be unique:
struct compare {
bool operator ()(const type_info* a, const type_info* b) const {
return a->before(*b);
}
};
std::map<const type_info*, std::string, compare> m;
void f() {
m[&typeid(int)] = "Hello world";
}
This is useful to define an order on typeinfo objects, e.g. to put them into a std::map. The obvious follow-up question is: why isn't it spelled operator<()? I don't know the answer to this question.
It gives an ordering.
That is required if you want to store values in some containers, like std::map.
Think of it as less-than (<) operator for type_info objects. If you ever wanted to store in ordered collection - such a set of map - you can use it to make an appropriate comparator. It's a reliable and preferred way, as opposed to, say, using type's name which might not be unique.

operator< overload for std::map's int type comparison? (I want it to sort in descending order..)

I have encountered a problem that I want to define a map, that is sorted internally by the first's descending order. if the first is not a primary type, like if it's a class, I can just overload the "<" within that class, but I don't know how to deal with the int type. Any suggestion?
Thanks a lot!!
Add a comparator:
#include <functional>
map<int, value_type, greater<int>> m;
The default is less<int>.
You can specify a comparator when you create the map (it's an optional constructor argument).
e.g.:
bool my_compare(int x, int y) { return y < x; }
std::map<int,T,bool(*)(int,int)> my_map(my_compare);
Notice that I've needed to explicitly specify a 3rd template parameter as well.
[Note: I would strongly advise that you don't overload the < operator to perform >...]
Look at the std::less implementation: http://www.cplusplus.com/reference/std/functional/less/
You may write own comparator and use it together with map.