Home > Software engineering >  c Overloaded operator () change std::set.find()
c Overloaded operator () change std::set.find()

Time:01-28

I'm learning std::set . Here is my codes:

#include <set>
#include <iostream>
using namespace std;
struct cmp{
    bool operator () (const int & a,const int & b) const {
        if(abs(a-b)<=3)
            return false;
        return a < b;
    }
};
set<int,cmp> q{1, 2, 10};
int main(){
    if(q.find(4)!=q.end())
        cout << 1;
    else
        cout << 2;
}

Output: 1

I use struct cmp to custom elements's sort rules,if abs(a-b)<=3 the new element will be delete.

But what surprised me was that q.find() had been changed.

I want to know why the output is 1,there is no 4 in q.

q.find(4) is to get a iterator where the element equals 4 in q , isn't it?

CodePudding user response:

You seem to expect that the set uses your comparator to check if two elements are equal (edit: on a second read, actually no, you are not checking for equality, but that does not change much on the answer). Thats not the case. A set does not care about equality. Two elements are considered equivalent when

 !(cmp(a,b) && !(cmp(b,a)

Consider the simple case of integers and cmp == std::less then this is just

 !(a < b) && !( b < a)

And with integers this can only be true when a == b. However, in general equivalence is not equality. In any case it is not cmp that should return true when two elements can be considered equivalent. cmp(a,b) should be true when a comes before b when the two are sorted.

This only works consistently when cmp implements a strict weak ordering as you can read here: https://en.cppreference.com/w/cpp/named_req/Compare

Your code has undefined behavior, because your comparator does not meet that requirement. It does not implement a strict weak ordering.

There is also no obvious way to fix that, because your way of comparing two elements cannot be used to sort them. Consider the following:

// 1 and 3 are equivalent because 
!cmp(1,3) && !cmp(3,1) == !false && !false == true

// 3 and 5 are equivalent because 
!cmp(3,5) && !cmp(5,3) == !false && !false == true

but

// 1 and 5 are not equivalent because 
!(cmp(1,5) && !cmp(5,1) == !true && !false == false

This is not consistent. Sloppy speaking, if two elements are equivalent it does not matter how you arrange them when sorting. It does not matter how 1 and 3 are arranged with respect to each other you can swap them with out breaking the ordering. Same holds for 3 and 5. But the order matters for 1 and 5. That is not consistent. Formally, it breaks the last requirement of a strict weak ordering as explained in the link above, as has been pointed out by Sebastian Redl in a comment.

  • Related