Using find in map with user defined class as key - c++

I have define a class Entry with 2 integer values a and b, and a vector of Entry 'e'. Now, I am going to define a map' m' of Entry and int where int is number of time that entry appear in vector. The thing is find function is considering Entry(1,2),Entry(1,12) and Entry(1,13) as one. Two entries should be same when both a and b are equal. But here if only a is equal its considering the two entries as same.
#include<iostream>
#include<map>
#include<vector>
using namespace std;
class Entry{
public:
int a;
int b;
Entry(int,int);
bool operator <(Entry);
bool operator >(Entry);
bool operator ==(Entry);
};
Entry::Entry(int x,int y):a(x),b(y){
}
bool Entry::operator ==(Entry e){
if(a==e.a && b==e.b)
return true;
else
return false;
}
bool Entry:: operator <(Entry e){
if(a<e.a)
return 1;
else
return 0;
}
bool Entry:: operator >(Entry e){
if(a>e.a)
return 1;
else
return 0;
}
int main(){
map<Entry,int> m;
vector<Entry> e;
e.push_back(Entry(1,2));
e.push_back(Entry(10,21));
e.push_back(Entry(1,13));
e.push_back(Entry(1,2));
e.push_back(Entry(1,12));
for(int i=0;i<e.size();i++){
if(m.find(e[i])==m.end())
m[e[i]]=1;
else{
m[e[i]]=m[e[i]]+1;cout<<e[i].a<<" "<<e[i].b<<" "<<m[e[i]]<<endl;
}
}
}

std::map is using the relational operator < for ordering and checking key uniqueness. Since your implementations of operator< use the a member only for comparing it will ignore b when evaluating key equality. operator== is not used from the map interface - it is replaced by !(a < b || b < a). You need something like
bool Entry::operator<(const Entry& e){
return a < e.a || a == e.a && b < e.b;
}
You could remove operator> and operator== or keep them if you need them for other purposes.

Related

C++ recursive struct comparator

I have created a struct to use as a key in a map to avoid having duplicate elements.
The struct contains pointers to children and siblings of its own type.
For the map, I have created a custom comparator that is supposed to recursively look at the element, the children and the siblings until a difference is found to make sure the elements are the same.
However, for some reason it is not working and Im still getting duplicates. After checking them out in the debugger, I concluded that they are indeed the exact same through and through so the problem must probably be somewhere in there.
This is the struct.
struct controlIdentifier
{
DWORD m_dwID;
DWORD m_dwDefaultID;
DWORD m_dwDisableID;
BYTE m_bType;
int m_nWidth;
int m_nHeight;
int m_nMargineH;
int m_nMargineV;
shared_ptr<controlIdentifier> m_pCHILD;
shared_ptr<controlIdentifier> m_pNEXT;
bool operator<(const controlIdentifier& id) const
{
if (m_dwDefaultID < id.m_dwDefaultID)
return true;
if (m_dwDisableID < id.m_dwDisableID)
return true;
if (m_bType < id.m_bType)
return true;
if (m_nWidth < id.m_nWidth)
return true;
if (m_nHeight < id.m_nHeight)
return true;
if (m_nMargineH < id.m_nMargineH)
return true;
if (m_nMargineV < id.m_nMargineV)
return true;
if (!m_pCHILD && id.m_pCHILD)
return true;
if (m_pCHILD && !id.m_pCHILD)
return false;
if (!m_pNEXT && id.m_pNEXT)
return true;
if (m_pNEXT && !id.m_pNEXT)
return false;
bool smaller = false;
if (m_pCHILD && id.m_pCHILD)
smaller = *m_pCHILD < *id.m_pCHILD;
if (!smaller)
{
if (m_pNEXT && id.m_pNEXT)
return *m_pNEXT < *id.m_pNEXT;
}
else
return smaller;
return false;
}
};
And this is how it's used.
struct cmpBySharedPtr {
bool operator()(const shared_ptr<controlIdentifier>& a, const shared_ptr<controlIdentifier>& b) const {
return *a < *b;
}
};
std::set<FRAMEDESC_SHAREDPTR> m_curFrames;
std::map<shared_ptr<controlIdentifier>, FRAMEDESC_SHAREDPTR, cmpBySharedPtr> m_serialFrames;
for (auto&& frame : m_curFrames)
{
shared_ptr<controlIdentifier> id;
makeIdentifiers(frame, id);
id->m_dwID = newId;
auto find = m_serialFrames.find(id);
if (find == m_serialFrames.end())
{
m_serialFrames.insert(std::pair(id, frame));
newId++;
}
}
m_dwID is not being compared on purspose.
Consider A = (child = 5, next = 6) and B = (child = 6, next = 5). Now A<B is true as (A.child < B.child) is true and it just returns that. Now consider B<A. B.child < A.child is false, so it checks the next fields.. Now B.next < A.next is true, so your comparison returns true.
So this is nonsensical -> A<B is true and B<A is true. This means your comparator is invalid.
The technical term for this is the comparator requires strict weak ordering - see https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings. Your comparator breaks the asymmetry requirement.
You can construct operator < by comparing field by field. But what you did is too little. Basically it shall look like this:
bool operator < (const A& left, const A& right)
{
if (left.firstField < right.firstField) return true;
if (right.firstField < left.firstField) return false; // this case is missing
if (left.secondField < right.secondField) return true;
if (right.secondField < left.secondField) return false; // this case is missing
....
return false;
}
You are missing cases when you can conclude, that for sure, left object is "greater" than right object.

How to overload == operator to see if two objects with a string vector are equal?

I am writing a class named StringSet which has vector<string> data and int length as its private members.
bool StringSet::operator == (StringSet d)
{
for (int i = 0; i < length; i++)
{
if (data[i] == d.data[i])
{
return true;
}
}
return false;
}
When I try calling this function like this,
StringSet doc1, doc2;
if (doc1 == doc2)
{
cout << "Both sentences are identical!\n";
}
I get an assertion failure saying vector subscript out of range, I know what that means but I don't know how it implies here. If anyone can point out an obvious mistake I have made that would be great as I am a newbie to c++.
It's simple
bool StringSet::operator == (const StringSet& d) const
{
return data == d.data;
}
std::vector and std::string have already comparison operators, therefore you don't have to implement something special.

Why does defining a struct with an operand< function that isn't const break things if you use it as a key in a Map?

I use a map and within that map, the type of the key is Coordinates:
struct Coordinates
{
int x;
int y;
Coordinates()
{
x = -1;
y = -1;
};
Coordinates(int a, int b) : x(a), y(b) {};
bool operator<(const Coordinates& otherCords) const
{
int thisSize;
int otherSize;
if (x >= y)
{
thisSize = x - y;
}
else
{
thisSize = y - x;
}
if (otherCords.x >= otherCords.y)
{
otherSize = otherCords.x - otherCords.y;
}
else
{
otherSize = otherCords.y - otherCords.x;
}
return thisSize < otherSize;
}
};
Took my quite a while to realize my operand function wasn't being detected by the map because it wasn't const. Why is that so?
Short answer: because that's the requirement of the map class.
Longer answer: The keys of a map are const and cannot be modified (because this could break the sort order the map relies on). Since the keys are constant values, any comparison function used with them needs to be const.
It must be const since you're not allowed to change the value of the key while it's in the map. In order to change a key, you must extract the element and reinsert it with a new key.
A bigger problem is that your operator< does not fulfill the strict weak ordering requirement - and many coordinates are likely to get rejected when you try to enter them into the map because an equal coordinate already exists. According to your function:
{0,0} == {1,1} == {2,2} == {3,3} // all where std::abs(x-y) == 0 are equal
{0,1} == {1,0} == {1,2} == {2,1} // all where std::abs(x-y) == 1 are equal
{0,2} == {2,0} == {1,3} == {3,1} // all where std::abs(x-y) == 2 are equal
...and so on...
One remedy could be to change the comparison function to:
bool operator<(const Coordinates& otherCords) const {
if(x==otherCords.x) return y < otherCords.y;
return x < otherCords.x;
}
Or simpler:
#include <tuple>
...
bool operator<(const Coordinates& otherCords) const {
return std::tie(x,y) < std::tie(otherCords.x, otherCords.y);
}

Sorting a Vector of a custom class with std::sort() causes a segmentation fault

I'd like to sort a vector of a custom class using std::sort() and overloading the < operator. Following the answer here: Sorting a vector of custom objects, I tried the following code:
class Evnt {
private:
int Day, Month;
string mydata;
public:
friend ifstream& operator >>(ifstream &in,Evnt &E){
char junk;
in >>junk>>E.Month>>junk>>E.Day;
getline(in,E.mydata);
return in;
}
bool operator<(const Evnt &E) const{
if(Month < E.Month)
return true;
else if(Day < E.Day)
return true;
return false;
}
};
int main(){
ifstream inpt("inputfile.txt")
Vector <Evnt> v;
Evnt tmpevnt;
while(intp>>tmpevnt)
v.push_back(tmpevent)
sort(v.begin(), v.end());
return 0;
}
The last line somewhat erratically causes segmentation faults. I followed the various examples fairly closely, and so am having issues figuring out what the problem is. It seems to only occur if I read a large number (~20+) items in.
std::sort requires a comparison operation that imposes Strict Weak Ordering.
That means that if a < b returns true, b < a must not return true.
Fix your comparison operator.
bool operator<(const Evnt &E) const{
if(Month < E.Month)
return true;
else if(Month == E.Month && Day < E.Day)
return true;
return false;
}
See this similar question for more information.

std::sort vector of struct invalid operator<

I have problem with strict weak ordering in the compare function in std::sort. I can't see why this would fail.
I have some nested structs:
struct date{
int day = 1;
int month = 1;
int year = 2017;
};
struct hhmmss{
int hours = 1;
int minutes = 1;
int seconds = 1;
};
struct dateAndTime {
date d;
hhmmss t;
};
struct Trade
{
/*
other unrelevant data
*/
dateAndTime timeClosed;
};
In my code, at some point I have a populated std::vector<Trade> which I want to sort.
My sort function:
void sortTradesByDate(std::vector<Trade>& trades){
std::sort(trades.begin(), trades.end(), compareDateAndTime);
}
My comparison function:
bool compareDateAndTime(const Trade& t1, const Trade& t2){
if (t1.timeClosed.d.year < t2.timeClosed.d.year)
return true;
else if (t1.timeClosed.d.month < t2.timeClosed.d.month)
return true;
else if (t1.timeClosed.d.day < t2.timeClosed.d.day)
return true;
else if (t1.timeClosed.t.hours < t2.timeClosed.t.hours)
return true;
else if (t1.timeClosed.t.minutes < t2.timeClosed.t.minutes)
return true;
else if (t1.timeClosed.t.seconds < t2.timeClosed.t.seconds)
return true;
return false;
}
When running the function, and debuging, my first item passed to compareDateAndTime() passes after returning true on one of the statements (months).
The next item returns true at hours comparison, but then I get a "Debug Assertion Failed!" with "Expression: invalid operator<".
Doing some googling, this has to do with strict weak ordering. But why does this fail when comparing int variables?
Your comparison function isn't implementing strict weak ordering
Consider this scenario:
t1: year=2017, month=2
t2: year=2016, month=5
compareDateAndTime(t1, t2) would return true.
You should proceed to compare month if and only if year is the same.
if (t1.timeClosed.d.year < t2.timeClosed.d.year)
return true;
if (t1.timeClosed.d.year > t2.timeClosed.d.year)
return false;
if (t1.timeClosed.d.month < t2.timeClosed.d.month)
return true;
if (t1.timeClosed.d.month > t2.timeClosed.d.month)
return false;
... and so forth ...
A nice way to leverage the Standard Library:
return std::tie(t1.timeClosed.d.year, t1.timeClosed.d.month) < std::tie(t2.timeClosed.d.year, t2.timeClosed.d.month);
You can add the missing members inside the std::tie's (it's a variadic template). This uses std::tuple's operator<, which is defined to do what you expect.