std::priority_queue different comparisons - c++

I want to construct three different priority_queue's that hold a class Thing and then sort each one differently by values that are held by the Thing. I know that I can define an operator method either internally, or a friend to the object, but is there a way to have it use different test method(s)? How do I tell it to use that method instead of the operator method? And how would the parameter list differ from creating an operator overload?

Like most STL containers, the priority_queue accepts a Compare class in its template arguments.
struct MyCompare1 {
bool operator()(const Thing& t1, const Thing& t2) {
// your logic here
}
};
std::priority_queue<Thing, std::vector<Thing>, MyCompare1> my_queue;

You can pass a custom comparator type that will be used instead of the default. This is a template parameter of the priority_queue.

You can specify your comparison function as the third parameter when defining the priority_queue. Note that the second parameter is the underlying container type, typically std::vector.
std::priority_queue<Thing> pq1;
std::priority_queue<Thing, std::vector<Thing>, std::greater<Thing> > pq2;
std::priority_queue<Thing, std::vector<Thing>, [your comparator here] > pq3;

Related

C++, why does std::set allow lower_bound() on a type different than that of the set elements, but only if I add less<> in the set declaration?

I have this class with an overloaded comparator:
struct Foo {
int x;
bool operator < (Foo other) const {
return x < other.x;
}
bool operator < (int val) const {
return x < val;
}
};
and I make a set on it:
set<Foo> s;
I'm allowed to call lower_bound() on a Foo, but not on an int (as you would expect).
s.lower_bound(Foo{3}); // works
s.lower_bound(3); //"no matching member function for call to 'lower_bound'
But if instead I declare s with a less<> :
set<Foo,less<>>
Both calls work:
s.lower_bound({3});
s.lower_bound(3);
Why?
why does std::set allow lower_bound() on a type different than that of the set elements
Because it is useful, and potentially more efficient.
Consider for example a set of std::strings. Creating (large) strings is expensive. If you have a string view, you can use std::less<> to compare the view to the strings of the set without having to construct a string from that view.
but only if I add less<> in the set declaration?
Because the second type parameter, to which you passed std::less<> as the argument is the functor that the set uses to compare the elements. std::less<> is what is able to compare objects of different types. The default comparator is std::less<T> which can only compare objects of type T. std::less<> isn't the default because it didn't exist when std::set was standardised.

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.

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.

C++ Code Clarification Needed

I'm trying to understand what the code below says:
struct compare_pq;
struct compare_pq {
bool operator() (Events *& a, Events *& b);
};
std::priority_queue<Events *, std::vector<Events *>, compare_pq> eventList;
i looked at what priority_queue is and how its declared but can't quit understand what compare_pq is doing in the priority_queue eventList. Also what does operator() do since i've never seen *& before and empty operator overloading operator()!
any help would be appreciated. Thank you
operator() is the function-call operator. It allows you to use an object of the class type as if it were a function, e.g.,
compare_pq my_comparator;
bool result = my_comparator(a, b);
Objects of class types that overload operator() are frequently called function objects or functors.
The third template parameter of std::priority_queue is for the comparison function. By default, the priority queue sorts its elements using std::less, which applies operator< to two elements. You can use any function (or function object) that takes two elements and returns a boolean indicating whether the first is smaller than the second. "Smaller" in this case is a relative term: the top() of the priority queue is the "largest" element currently in the queue.
In this case, you need to use a custom comparison function because the priority queue is storing pointers, so by default it would sort the elements by pointer value. The custom comparator (probably) dereferences the pointers and performs some comparison on the pointed-to objects.
Events*& is just a reference to a pointer to an Events object. It really doesn't need to be passed by reference. Since it's just a pointer, it can be passed by value (e.g., Events*). If you do choose for some reason to use a reference it should be a const reference.
*& is a reference to a pointer. It works like any other kind of reference. In less sophisticated C++ code you might see a double pointer (**) used.
compare_pq is a functor used to compare Event pointers. In this case, priority_queue would likely be instantiating a compare_pq whenever a comparison is needed.
Event * a = new Event();
Event * b = a;
compare_pq foo;
bool result = foo(a, b);
operator() is not empty. You're looking at a declaration. It must be defined somewhere else if it is to be instantiated.
I'll try to answer the question why a functor is used. It's just a guess of course, as I'm not the author of the code, but I saw discussions about it at least a few times and the consensus seems to be, that functors enable or at least make it easier to inline the comparison code.
Functors are structs (or classes) and in general are more flexible than regular functions because they can have some members, that store some state, which can be used by operator(). In this case this advantage isn't used, so the functor was most probably used to enable (or help) in inlining or just because the author was used to this common pattern.
Why would it help in inlining? Let's look at a simple example. Lets take std::sort
template <class RandomAccessIterator, class Compare>
void sort ( RandomAccessIterator first, RandomAccessIterator last, Compare comp );
Imagine You want to sort std::vector<int> and You want to provide Your custom comparators.
struct MyStructComp1
{
bool operator()(int lhs, int rhs) const { /*...*/}
};
struct MyStructComp2
{
bool operator()(int lhs, int rhs) const { /*...*/}
};
bool myFunctComp1 (int lhs, int rhs) const { /*...*/}
bool myFunctComp2 (int lhs, int rhs) const { /*...*/}
Now You can use the sort temeplate in the following ways
sort(myvector.begin(), myvector.end(), MyStructComp1()); // 1
sort(myvector.begin(), myvector.end(), MyStructComp2()); // 2
sort(myvector.begin(), myvector.end(), myFunctComp1); // 3
sort(myvector.begin(), myvector.end(), myFunctComp2); // 4
Here are the function the compiler creates form the template
sort<vector<int>::iterator, MyStrucComp1> // 1
sort<vector<int>::iterator, MyStrucComp2> // 2
sort<vector<int>::iterator, bool (*) (int lhs, int rhs)> // 3, // 4
Since the Compare parameter in the sort template is a type, and functors are types, the compiler creates a different function for every functor supplied as a template argument. sort<vector<int>::iterator, MyStrucComp1> and
sort<vector<int>::iterator, MyStrucComp2> are two different functions. So when sort<vector<int>::iterator, MyStrucComp1> is created, it is known exactly what the comparing code is and the comparator can be simply inlined.
Functions myFunctComp1 and myFunctComp2 however are of exactly the same type:
bool (*) (int lhs, int rhs) and the compiler creates one function sort<vector<int>::iterator, bool (*) (int lhs, int rhs)> for all comparing functions of type bool (*) (int lhs, int rhs). I saw opinions, that inlining is possible anyway in this situation, but I have no idea how.
It is possible to create templates with a pointer to function as a template parameter as it's a compile time constant, but it's ugly and constants can't be deduced from the function arguments. For example if sort was defined as:
template <class RandomAccessIterator,
bool (*comparer) (typename RandomAccessIterator::value_type, typename RandomAccessIterator::value_type)>
void sort ( RandomAccessIterator first, RandomAccessIterator last) {/* */}
You would have to call it like this
sort<std::vector<int>::iterator, myFunctComp1>(myvector.begin(), myvector.end());
You would get a different sort for every comparing function, but functors are much more convenient.

Requirement for key in std::multimap

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.