Why can't the overload operator<() implement the comparison function? - c++

I was looking at this. The author first defined operator<() in my_data and said "everything is normal". After adding a member variable, he said "operator<() does not really implement a comparison operation".
I want to know what is the difference between the two and why the former is wrong?
struct my_data
{
std::string key;
std::string value;
//first
bool operator<(const my_data data)const {
return key < data.key;
}
};
//second
struct Compare
{
bool operator()(const my_data& l, const my_data& r)
const {
return l.key < r.key;
}
};
from there

With
struct my_data
{
std::string key;
std::string value;
//first
bool operator<(const my_data data)const {
return key < data.key;
}
};
std::set<my_data> data;
You can use the class with a std::set, but your operator < isn't using all of the object, it is just comparing a single field. That is what the author of the video is complaining about. They take issue that it only compares one field of the object, and not the whole state of the object. By switching to
struct my_data
{
std::string key;
std::string value;
};
struct Compare
{
bool operator()(const my_data& l, const my_data& r)
const {
return l.key < r.key;
}
};
std::set<my_data, Compare> data;
There is no longer a "lying" operator <, but instead a custom comparator that compares just what you want.

Related

Hash function for a smart pointer class as key for an unordered map

So, I have a pointer wrapper class which stores only a pointer and I need to use this class instance as a key in an unordered map. I currently have a similar setup with this pointer wrapper instances as keys to a std::map by overriding bool operator< but for an unordered map setup I would need to override two other operators == and (). I've figured how == operator implementation would be. but not sure about an implementation for the () operator. what sort of hash implementation setup should I be doing in this case? I've checked in places and most examples cover non pointer cases and they use two Key items and form hash for each and compare them for () implementation.
template <class T>
class PointerWrap{
public:
T* pointer;
bool operator<(const PointerWrap& other)const{return *pointer < *other.pointer;}
bool operator==(const PointerWrap& other)const{return *pointer == *other.pointer;}
//size_t operator()(const PointerWrap& other)const{return (*pointer)(*other.pointer);}
};
class VarType{
bool operator<(const VarType& other)const{return this < &other;}
bool operator==(const VarType& other)const{return this == &other;}
size_t operator()(const VarType& other)(.?.?.}
};
//Desired setup.
std::unordered_map<PointerWrap<VarType>,Value> mymap;
Since you seem to need a hash function only for using PointerWrappers in an unordered map, the hash function in the standard library should serve you well. (But these are not cryptographically secure hash functions so don't use them for anything else). Here is some code to show how to do this:
#include <unordered_map>
#include <iostream>
template <class T>
class PointerWrap {
public:
T* pointer;
bool operator<(const PointerWrap& other)const { return *pointer < *other.pointer; }
bool operator==(const PointerWrap& other)const { return *pointer == *other.pointer; }
size_t operator()(const PointerWrap& other) const {return (*pointer)(*other.pointer);}
};
class VarType {
public: // PointerWrap has no access to these operators without a public access specifier
bool operator<(const VarType& other)const { return this < &other; }
bool operator==(const VarType& other)const { return this == &other; }
// Pointless to hash a object without any data
std::size_t operator()(const VarType& other)const {
return 0;
}
};
// Specialization of std::hash for PointerWrap<T>
template<typename T>
class std::hash<PointerWrap<T>> {
public:
size_t operator()(PointerWrap<T> v) const{
return std::hash<T*>()(v.pointer);
}
};
int main() {
// your desired setup compiles.
std::unordered_map<PointerWrap<VarType>, int> mymap;
PointerWrap<VarType> a;
mymap[a] = 5;
std::cout << mymap[a] << std::endl;
return 0;
}

Writing a custom comparator for a C++ map which has a custom defined key

I'm trying to write a custom comparator for a C++ map which has a custom defined key.
struct key { int year; int no; };
map<key, detail, compare> details_map;
if the year values are equal, it must compare the no values.
I'm trying to figure out a way to write a comparator that can compare both values. So far, I am only able to write a comparator which compares one value.
struct Compare{bool operator()(const key &lhs,const key &rhs)const{return lhs.year<rhs.year;}}
Can someone please explain how a comparator works in a map?
Also, is it possible to write the comparator as a function?
Inside your operator(), simply compare the no values if the year values are equal:
struct Compare {
bool operator()(const key &lhs, const key &rhs) const {
if (lhs.year == rhs.year) {
return lhs.no < rhs.no;
}
return lhs.year < rhs.year;
}
};
And yes, a comparator can be implemented as a standalone function instead:
bool Compare (const key &lhs, const key &rhs) {
if (lhs.year == rhs.year) {
return lhs.no < rhs.no;
}
return lhs.year < rhs.year;
}
Alternatively, you can have your comparator use std::tie() to compare your key fields. See #Jarod42's answer.
Though, it would make more sense to implement operator< for your key type instead:
struct key {
int year;
int no;
bool operator<(const key &rhs) const {
if (year == rhs.year) {
return no < rhs.no;
}
return year < rhs.year;
}
};
Or
struct key {
int year;
int no;
};
bool operator<(const key &lhs, const key &rhs) {
if (lhs.year == rhs.year) {
return lhs.no < rhs.no;
}
return lhs.year < rhs.year;
}
Then you don't need a separate comparator:
map<key, detail> details_map;
std::tie allows simple lexicographical comparison:
struct Compare {
bool operator()(const key& lhs, const key& rhs) const {
return std::tie(lhs.year, lhs.no) < std::tie(rhs.year, rhs.no);
}
};
Method/function as_tuple might be interesting to avoid some repetitions:
struct key { int year; int no; };
auto as_tuple(const key& k) { return std::tie(k.year, k.no); }
struct Compare {
bool operator()(const key& lhs, const key& rhs) const {
return as_tuple(lhs) < as_tuple(rhs);
}
};

Sorting list of shared pointers

Given the class
class objects {
public:
bool compareArea (const objects& obj) const { return this->area < obj.area; }
private:
double area;
};
I want to sort a
list<shared_ptr<objects>> myObjects;
I cannot use a lambda (since my toolchain's C++11 support is incomplete). Thus, I tried the following:
using namespace placeholders;
myObjects.sort(bind(&objects::compareArea,_1,_2));
This line is called from another file (not from a class member!). The problem is, that compareArea requires two objects as input. But I give two shared pointer to objects to it. Is there an easy way of how to include the dereferencing of the pointers into the sort-call? I want the objects::compareArea(..) function to stay as it is. I do not want this kind of solution
bool compareArea (const shared_ptr<objects>& ptr1, const shared_ptr<objects>& ptr2) {
return ptr1->area > ptr2->area;
}
// in same source-file:
myObjects.sort(bind(compareArea,_1,_2));
where compareArea is no member-function of objects. Actually an operator overloading of < would be my favourite solution.
I would strongly suggest that you never store any kind of pointer in a container.
Instead, make a handle class which supports the required arithmetic and comparison operators.
It makes for code that's easier to reason about:
class objects {
public:
objects(double w, double h) : area(w * h) {}
bool operator<(const objects& r) const { return this->area < r.area; }
private:
double area;
};
struct object_handle
{
object_handle(shared_ptr<objects> const& ptr) : ptr_(ptr) {}
static object_handle create(double w, double h) { return make_shared<objects>(w,h); }
bool operator < (object_handle const& r) const {
return *ptr_ < *r.ptr_;
}
shared_ptr<objects> ptr_;
};
int main() {
std::vector<object_handle> mylist;
mylist.push_back(object_handle::create(10, 7));
mylist.push_back(object_handle::create(2, 5));
std::sort(mylist.begin(), mylist.end());
}
Lambdas are just syntactic sugar for a class with operator(), so you can very easily write one directly (especially if you don't need captures):
struct Comparator
{
bool operator() (const shared_ptr<objects> &lhs, const shared_ptr<objects> &rhs) const
{
return lhs->compareArea(*rhs);
}
};
myObjects.sort(Comparator());

The priority_queue of STL in c++

I want to construct two priority queue that has different compare method(there are two reverse priority methods named cmp1 and cmp2)
My program can't go through the compiler check.Why does such error happen and is there any better solution?
#include <iostream>
#include <queue>
#include <string>
using namespace std;
struct item
{
string name;
string sex;
string id;
double score;
friend istream& operator >> (istream &is,item& data)
{
is>>data.name>>data.sex>>data.id>>data.score;
}
/*friend bool operator < (item& a,item& b)
{
return a.score<b.score;
}*/
};
struct cmp1{
operator bool()(item& x,item& y)
{
return x.score>y.score;
}
};
struct cmp2
{
operator bool()(item& x,item& y)
{
return x.score<y.score;
}
};
int main()
{
priority_queue<item,vector<item>,cmp1> boys;
priority_queue<item,vector<item>,cmp2>girls;
item temp;
int num;
cin>>num>>temp;
for(int i=0;i<num;i++)
{
if(temp.sex=="M")
boys.push(temp);
else
girls.push(temp);
}
return 0;
}
I'm going to take a wild guess at the problem.... your comparison functors are incorrect. Instead of overloading operator bool, you need to overload the function call operator, i.e.
struct cmp1{
bool operator()(const item& x, const item& y)
{
return x.score>y.score;
}
};
struct cmp2
{
bool operator()(const item& x, const item& y)
{
return x.score<y.score;
}
};
(Perhaps this was what you intended, but just got the syntax a little wrong?)
Actually, I think the best way to do it is to use std::less and std::greater. If you have overloaded operator< and operator> for your class, you can do it like this:
std::priority_queue<item, std::vector<item>, std::greater<item>> boys;
std::priority_queue<item, std::vector<item>, std::less<item>> girls;
That way you don't have to write the functor. Don't forget to #include <functional>.
Also, don't forget that the operators have to be overloaded taking const ref arguments (you can also take them by value, but that's not usually a good idea), and as const methods, like in:
bool operator<(const item& i) const {
return value < i.value;
}
bool operator>(const item& i) const {
return value > i.value;
}
Change your operator functions to this:
struct cmp1{
bool operator()(item& x,item& y)
{
return x.score>y.score;
}
};
struct cmp2
{
bool operator()(item& x,item& y)
{
return x.score<y.score;
}
};
You have defined the methods in the comparison classes wrong.
Try the following code:
struct cmp1{
bool operator()(item& x,item& y)
{
return x.score>y.score;
}
};
You have defined the methods as operator bool() ....
Also it is nice to add const to the parameters, as const item& x for showing that you won't change their values, as well a const at the end of the function definition:
bool operator()(const item& x, const item& y) const {...}
for showing that you won't change the member fields too.
I will write the third correct version of the predcate
struct cmp1
{
bool operator()( const item &x, const item &y ) const
{
return x.score > y.score;
}
};
struct cmp2
{
bool operator()( const item &x, const item &y ) const
{
return x.score < y.score;
}
};

How use std::multiset with multiple comparator function?

Good afternoon, I have a C++ class Range which implements a operator < for use by std::multiset<Range> ranges_type.
Since the multiset constructor don't specify a a custom comparator functor, it uses the std::less operator <.
However, I need to use a second comparator functor for std::multiset ranges_type. Specifically, I would specify a second comparator:
std::multiset<Range, PointerCompare> where struct PointerCompare looks this :
struct PointerCompare{
bool operator()(const Range& a, const Range& b) const {
return (a.mPtr == b.mPtr)
}
Is it possible to use std:multiset with multiple comparator functions or is there a workaround? Thank you
The class Range looks this:
class Range {
public:
explicit Range(int item){
mLow = item;
mHigh = item;
mPtr = 0;
}
Range(int low, int high, char* ptr = 0,char* mapptr = 0){
mLow = low;
mHigh = high;
mPtr = ptr;
}
Range(void){
mLow = 0;
mHigh = 0;
mPtr = 0;
}
Range(const Range& r):
mLow(r.mLow),
mHigh(r.mHigh),
mPtr(r.mPtr)
{
}
bool operator==(const Range& rhs) const{
return (mLow <= rhs.mLow && mHigh >= rhs.mHigh);
}
bool operator<(const Range& rhs) const{
return mHigh < rhs.mHigh;
}
int low() const { return mLow; }
int high() const { return mHigh; }
char* getPtr() const { return mPtr; }
private:
int mLow;
int mHigh;
char* mPtr;
}; // class Range
Sounds almost like you'd be better if you used something from Boost::MultiIndex rather than trying to force several different comparator functions onto a std::multiset. They have a bunch of different container types (see here.) In particular I'd look at the ordered_indices versions.
I may have found a workaround for multiple comparator functions: Here it is:
Range targetRange = Range(PreviousNCopy,PreviousN, TmpPrevMapPtr);
bool Found = std::binary_search( ranges_type.begin(), ranges_type.end(),
targetRange, MyComparator() );
where: MyComparator is a struct :
struct MyComparator {
bool operator () ( const Range& d1, const Range& d2 ) const
{
return d1.getPtr() < d2.getPtr();
}
};
std::binary_search take o(log n) time but the std::multiset ranges_type must always remain sorted. Thank you.