std::sort vector of struct invalid operator<

408 views Asked by At

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?

2

There are 2 answers

1
rustyx On BEST ANSWER

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

0
rubenvb On

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.