0

Why is equality needed for rel_ops? Isn't "<" enough?

a==b => !(a<b) && !(b<a)

It might be a very stupid question. Am I missing something?

Radu C
  • 303
  • 2
  • 11
  • 2
    Well, if the standard did that, it will make things like `NaN` equal to `NaN`, which is debatable... And No, you cannot exactly compare this with how `std::map` finds a key. Because the domain for the [key has](https://stackoverflow.com/questions/8096817/is-nan-a-valid-key-value-for-associative-containers) constraints – WhiZTiM Feb 16 '19 at 15:31
  • 2
    Not quite about `rel_ops`, but this has been a hot topic with `<=>`. You can read more in papers ([`<=> != ==`](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1185r0.html), [I did not order this! Why is it on my bill?](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1190r0.html)) and blogs ([Improvements to <=>](https://brevzin.github.io/c++/2018/11/12/improve-spaceship/)). For reference, `<=>` has already been changed because of this. – chris Feb 16 '19 at 16:19
  • @chris: Thanks! Very interesting read – Radu C Feb 19 '19 at 15:25

2 Answers2

2

Why is equality needed for rel_ops? Isn't "<" enough?

a==b => !(a<b) && !(b<a)

Because this is not true in general. If rel_ops would work only for relational operators that follow that logic it would be rather limited.

I guess what you have in mind is the weak ordering required for the < operator for associative containers. From cppreference:

Everywhere the standard library uses the Compare requirements, uniqueness is determined by using the equivalence relation. In imprecise terms, two objects a and b are considered equivalent (not unique) if neither compares less than the other: !comp(a, b) && !comp(b, a).

In simple terms: Whether two keys are considered the "same" is only determined by requiring ! (a < b) && ! (b < a). Hence you only need to supply a < for associative containers and no operator== to decide whether two keys are the same. However, equivalence (!(a<b)&&!(b<a)) is not necessarily the same as equality (a==b).

For example when you use this

struct my_key {
    int a;
    int b;
    bool operator< (const key_type& other) {
        return a < other.a;   // not comparing b !
    }
};

as a key of a std::map then my_key{1,0} and my_key{1,2} are equivalent ("same key"), even though they are not equal. As another example, consider a Point in spherical coordinates where we choose to have a < b when a is closer to the origin than b:

struct Point {
    double radius;
    double angle;
    bool operator<(const Point& other) { 
        return radius < other.radius; 
    }
    bool operator==(const Point& other) {
        return (radius == other.radius) && (angle == other.angle);
    }
}

Also here all three a < b,b < a and a == b can be false at the same time.

Also note that (from cppreference)

As of C++20, std::rel_ops are deprecated in favor of operator<=>.

For the starship operator <=> you can choose between

std::strong_ordering 
std::weak_ordering
std::partial_ordering
std::strong_equality 
std::weak_equality  

Weak ordering is what is required eg for std::map (eg my_key or Point), while for strong ordering equivalence and equality are basically the same. For more details and examples I refer you to this.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
1

Yes, actually you are missing sth. And it is very fundamental. It is not about C++ or programing. It's about math. According to math, your statement is true if and only if "<" defines a strict weak ordering on its operand domain. Not every user-defined type with "less" rel_op has a weak ordering.

Red.Wave
  • 2,790
  • 11
  • 17