294

I'm learning about operator overloading in C++, and I see that == and != are simply some special functions which can be customized for user-defined types. My concern is, though, why are there two separate definitions needed? I thought that if a == b is true, then a != b is automatically false, and vice versa, and there is no other possibility, because, by definition, a != b is !(a == b). And I couldn't imagine any situation in which this wasn't true. But perhaps my imagination is limited or I am ignorant of something?

I know that I can define one in terms of the other, but this is not what I'm asking about. I'm also not asking about the distinction between comparing objects by value or by identity. Or whether two objects could be equal and non-equal at the same time (this is definitely not an option! these things are mutually exclusive). What I'm asking about is this:

Is there any situation possible in which asking questions about two objects being equal does make sense, but asking about them not being equal doesn't make sense? (either from the user's perspective, or the implementer's perspective)

If there is no such possibility, then why on Earth does C++ have these two operators being defined as two distinct functions?

Baum mit Augen
  • 46,177
  • 22
  • 136
  • 173
BarbaraKwarc
  • 2,511
  • 2
  • 10
  • 17
  • Comments are not for extended discussion; this conversation has been [moved to chat](http://chat.stackoverflow.com/rooms/114728/discussion-on-question-by-barbarakwarc-are-and-mutually-dependent). – George Stocker Jun 15 '16 at 10:23
  • 13
    Two pointers may both be null but not necessarily equal. – Ali Caglayan Jun 15 '16 at 10:50
  • 2
    Not sure if it makes sense here, but reading this made me think of 'short circuit' issues. For example, one could define that `'undefined' != expression` is always true (or false, or undefined), regardless of whether expression can be evaluated. In this case `a!=b` would return the correct result as per definition, but `!(a==b)` would fail if `b` cannot be evaluated. (Or take a lot of time if evaluating `b` is expensive). – Dennis Jaheruddin Jun 15 '16 at 11:24
  • 2
    What about null != null and null == null? It can be both... so if a != b that doesn't always mean a == b. – zozo Jun 16 '16 at 05:25
  • 1
    The simplest way to put it (too short to be an answer, IMO), is that while one is usually defined in terms of the other, this isn't mandatory because there's always the possibility that you'll run into a weird situation where you _don't_ want them to be intrinsically tied together. The standard gives you the freedom to define them individually in whichever manner is best for the situation, to prevent the programmer from being limited if such a situation comes up. This is also why you're free to define, for example, `operator+` in terms of `operator-=` instead of `operator+=`, if you want to. – Justin Time - Reinstate Monica Jun 16 '16 at 19:21
  • (Not suggesting that anyone _actually_ define `operator+` in terms of `operator-=`, just that you're _allowed_ to do so, even though `operator+` is ideally defined in terms of `operator+=`.) – Justin Time - Reinstate Monica Jun 16 '16 at 19:22
  • 4
    An example from javascript `(NaN != NaN) == true` – chiliNUT Jun 19 '16 at 06:11
  • How would you define `==` and `!=` for incomparable objects. In such case, both should be false. – Šimon Tóth Jun 19 '16 at 11:25
  • 1
    @AliCaglayan "Two null pointer values of the same type shall compare equal." [conv.ptr]p1. Or what did you mean by that, their bit patterns? – Columbo Jun 19 '16 at 18:28
  • 2
    http://eel.is/c++draft/expr.eq#2 – Ven Jun 19 '16 at 18:33
  • @chiliNUT that is not "from javascript", it is true in most languages including c++ – Brennan Vincent Apr 24 '19 at 07:13

15 Answers15

273

You would not want the language to automatically rewrite a != b as !(a == b) when a == b returns something other than a bool. And there are a few reasons why you might make it do that.

You may have expression builder objects, where a == b doesn't and isn't intended to perform any comparison, but simply builds some expression node representing a == b.

You may have lazy evaluation, where a == b doesn't and isn't intended to perform any comparison directly, but instead returns some kind of lazy<bool> that can be converted to bool implicitly or explicitly at some later time to actually perform the comparison. Possibly combined with the expression builder objects to allow complete expression optimisation before evaluation.

You may have some custom optional<T> template class, where given optional variables t and u, you want to allow t == u, but make it return optional<bool>.

There's probably more that I didn't think of. And even though in these examples the operation a == b and a != b do both make sense, still a != b isn't the same thing as !(a == b), so separate definitions are needed.

  • 72
    Expression building is a fantastic practical example of when you'd want this, which doesn't rely on contrived scenarios. – Oliver Charlesworth Jun 13 '16 at 22:53
  • 6
    Another good example would be vector logical operations. You'd rather one pass through the data computing `!=` instead of two passes computing `==` then `!`. Especially back in the day where you couldn't rely on the compiler to fuse the loops. Or even today if you fail to convince the compiler your vectors don't overlap. –  Jun 13 '16 at 23:06
  • 42
    "You may have expression builder objects" -- well then operator `!` can also build some expression node and we're still fine replacing `a != b` with `!(a == b)`, so far as that goes. Same goes for `lazy::operator!`, it can return `lazy`. `optional` is more convincing, since the logical truthiness of for example `boost::optional` depends on whether a value exists, not on the value itself. – Steve Jessop Jun 13 '16 at 23:13
  • 43
    All that, and `Nan`s - please remember the `NaN`s; – jsbueno Jun 14 '16 at 04:16
  • 9
    @jsbueno: it's been pointed out further down that NaNs aren't special in this regard. – Oliver Charlesworth Jun 14 '16 at 06:06
  • @hvd expressions aside, how is (a != b) not the same thing as !(a == b)? – ya23 Jun 14 '16 at 10:16
  • 1
    @SteveJessop For expression builder objects, `a != b` and `!(a == b)` are not necessarily equivalent. You may want a faithful representation of the original expression, or the expression itself may represent one of the other exceptions. For `lazy`, the implementation I had in mind was that it wouldn't have overloaded operators, just implicit conversions to `T`. The implementation you're thinking of would be possible too, but assuming you extend it to other operators than `!` as well, that basically means you're building expressions again. –  Jun 14 '16 at 12:50
  • 2
    @ya23 I hope my response to Steve Jessop makes it clearer. In the `lazy` I had in mind, `a == b` would return a `lazy`. Applying `!` would block until its value is known, and then negate that value. Other approaches to `lazy` are perfectly valid too though. –  Jun 14 '16 at 12:52
  • This is a perfect example of why great programmers often make poor mathematicians (and vice-versa). With all due respect to programmers. – Todd Wilcox Jun 14 '16 at 18:16
  • 1
    @hvd I didn't mean the language *automatically rewriting* `a != b` as `!(a==b)`, but the *results* of both expressions being *equivalent*. I accepted your answer though for opening my eyes that `==` and `!=` may be overloaded to return a different type than `bool`. Using them as expression tree builders sounds cool :) But the question still stands: even if I use them as expression builders, isn't it that **I still need to overload *both*** because it wouldn't make sense to have one but not the other? – BarbaraKwarc Jun 14 '16 at 18:34
  • 2
    @BarbaraKwarc In my examples, the results of both expressions are not necessarily equivalent. :) Yes, I think I agree that it wouldn't make sense to have one operator working and not the other, or at least if there is a case where that makes sense I cannot think of it. –  Jun 14 '16 at 18:52
  • @Hurkyl: Things are the same with modern fixed-size short-vector SIMD: [Agner Fog's SSE/AVX wrapper class library](http://www.agner.org/optimize/#vectorclass) returns a boolean vector result for comparison operators. (vector as in `__m128i`, not `std::vector`). So there are functions like `static inline Vec16cb operator == (Vec16c const & a, Vec16c const & b) { return _mm_cmpeq_epi8(a,b); }`, where `Vec16c` wraps a `__m128i` with a class of sixteen 1-char elements, and `Vec16cb` is a subclass of that where the elements are 0 or -1. – Peter Cordes Jun 15 '16 at 13:49
  • @BarbaraKwarc: For SSE integer vectors, `operator ==` can be implemented efficiently, but `operator !=` can't be. SSE provides `pcmpeq` instructions for various element sizes, but only AMD's XOP extensions provide a compare for not-equal. Without that, the best you can do for != is two instructions: `pcmpeqb` and then all flip the bits in packed-compare result with an XOR with a vector of all ones. (Or another compare against a vector of all zeros, so 0 -> -1 and -1 -> 0). So it's 2 instructions plus an all-ones vector constant. – Peter Cordes Jun 15 '16 at 14:00
  • 2
    Often you can just invert the logic of whatever you do next, e.g. reverse the arguments to a blend instruction. This would be a valid reason to *not* provide an `operator !=`. Agner Fog's VCL does provide one, leaving it up to the programmer to remember that `!=` is slower and should be avoided when possible, but it would be a valid design choice to not do that, and force users of the library to write `!(a == b)` if they really need the inverse. Or to provide `!=` only when building for targets supporting AMD's XOP extensions. (That last being not a *good* design choice, but valid.) – Peter Cordes Jun 15 '16 at 14:04
  • 1
    Also, three-valued logic such as SQL has with `NULL` (although that's sort of covered by your `optional` example). – dan04 Jun 15 '16 at 23:43
  • Infinities do not compare normally – joojaa Jun 16 '16 at 07:22
  • I can't imagine thing that I want less than some library/class giving me a different result for `a == b` and `!(a != b)`. One just should not overload this operators if it can't follow the basic logic rule. Create a special function or whatever but please don't add so big confusion... – oliora Jun 29 '16 at 22:54
111

If there is no such possibility, then why on Earth does C++ have these two operators being defined as two distinct functions?

Because you can overload them, and by overloading them you can give them a totally different meaning from their original one.

Take, for example, operator <<, originally the bitwise left shift operator, now commonly overloaded as an insertion operator, like in std::cout << something; totally different meaning from the original one.

So, if you accept that the meaning of an operator changes when you overload it, then there is no reason to prevent user from giving a meaning to operator == that is not exactly the negation of operator !=, though this might be confusing.

John Kugelman
  • 307,513
  • 65
  • 473
  • 519
shrike
  • 4,326
  • 2
  • 20
  • 33
  • 18
    This is the only answer that makes practical sense. – Sonic Atom Jun 14 '16 at 22:47
  • 2
    To me it seems like you have the cause and effect backwards. You can overload them separately because `==` and `!=` exist as distinct operators. On the other hand, they probably don't exist as distinct operators because you can overload them separately, but due to legacy and convenience (code brevity) reasons. – nitro2k01 Jun 18 '16 at 12:16
60

My concern is, though, why are there two separate definitions needed?

You don't have to define both.
If they are mutually exclusive, you can still be concise by only defining == and < alongside std::rel_ops

Fom cppreference:

#include <iostream>
#include <utility>

struct Foo {
    int n;
};

bool operator==(const Foo& lhs, const Foo& rhs)
{
    return lhs.n == rhs.n;
}

bool operator<(const Foo& lhs, const Foo& rhs)
{
    return lhs.n < rhs.n;
}

int main()
{
    Foo f1 = {1};
    Foo f2 = {2};
    using namespace std::rel_ops;

    //all work as you would expect
    std::cout << "not equal:     : " << (f1 != f2) << '\n';
    std::cout << "greater:       : " << (f1 > f2) << '\n';
    std::cout << "less equal:    : " << (f1 <= f2) << '\n';
    std::cout << "greater equal: : " << (f1 >= f2) << '\n';
}

Is there any situation possible in which asking questions about two objects being equal does make sense, but asking about them not being equal doesn't make sense?

We often associate these operators to equality.
Although that is how they behave on fundamental types, there is no obligation that this be their behaviour on custom data types. You don't even have to return a bool if you don't want to.

I've seen people overload operators in bizarre ways, only to find that it makes sense for their domain specific application. Even if the interface appears to show that they are mutually exclusive, the author may want to add specific internal logic.

(either from the user's perspective, or the implementer's perspective)

I know you want a specific example,
so here is one from the Catch testing framework that I thought was practical:

template<typename RhsT>
ResultBuilder& operator == ( RhsT const& rhs ) {
    return captureExpression<Internal::IsEqualTo>( rhs );
}

template<typename RhsT>
ResultBuilder& operator != ( RhsT const& rhs ) {
    return captureExpression<Internal::IsNotEqualTo>( rhs );
}

These operators are doing different things, and it would not make sense to define one method as a !(not) of the other. The reason this is done, is so that the framework can print out the comparison made. In order to do that, it needs to capture the context of what overloaded operator was used.

Trevor Hickey
  • 31,728
  • 22
  • 131
  • 236
  • 14
    Oh my, how could I *not* know about `std::rel_ops`? Thank you a lot for pointing that out. – Daniel Jour Jun 13 '16 at 22:52
  • 5
    Near-verbatim copies from cppreference (or anywhere else) should be clearly marked and properly attributed. `rel_ops` is horrible anyway. – T.C. Jun 13 '16 at 22:53
  • @T.C. Agreed, I'm just saying its a method OP can take. I don't know how to explain rel_ops any simpler than the example shown. I linked to where it is, but posted code since the reference page could always change. – Trevor Hickey Jun 13 '16 at 22:56
  • 4
    You still need to make it clear that the code example is 99% from cppreference, rather than your own. – T.C. Jun 14 '16 at 00:53
  • @T.C. ok, added a cppreference despite the code being fundamental. – Trevor Hickey Jun 14 '16 at 01:26
  • @TrevorHickey Well, there might not be a "physical" obligation in the language for `==` and `!=` to be comparisons, but there's still a "common sense" obligation: redefining them to do something else than comparison would break their semantics and make them non-intuitive to use, because it breaks the users' expectations and requires them to look through manuals to figure out what they're supposed to mean for that particular context, which doesn't sound amusing. – BarbaraKwarc Jun 14 '16 at 21:30
  • @TrevorHickey As for the Catch framework, yeah, sounds cool ;) But there's a... catch :P It still defines both `==` and `!=` to mean something complementary to each other. What I'm curious about is if there are cases in which defining `==` but not `!=` (or vice versa) would make sense for some type. – BarbaraKwarc Jun 14 '16 at 21:32
  • 2
    Std::relops seems to have fallen out of favor. Check out boost ops for something more targeted. – JDługosz Jun 15 '16 at 02:56
  • I didn't know about `rel_ops` either. This would be a fantastic answer to [an old question of mine](http://stackoverflow.com/q/29269124/1858225). – Kyle Strand Jun 17 '16 at 05:41
44

There are some very well-established conventions in which (a == b) and (a != b) are both false not necessarily opposites. In particular, in SQL, any comparison to NULL yields NULL, not true or false.

It's probably not a good idea to create new examples of this if at all possible, because it's so unintuitive, but if you're trying to model an existing convention, it's nice to have the option to make your operators behave "correctly" for that context.

Jander
  • 4,481
  • 19
  • 18
  • That is not true (at least for NaN). – Oliver Charlesworth Jun 14 '16 at 06:07
  • 4
    Implementing SQL-like null behavior in C++? Ewwww. But I suppose it is not something I think should be banned in the language, however distasteful it might be. –  Jun 14 '16 at 07:58
  • 1
    @dan1111 More importantly, some flavors of SQL may well be coded in c++, so the language needs to support their syntax, no? – Joe Jun 14 '16 at 15:40
  • 1
    Correct me if I'm wrong, I'm just going off of [wikipedia](https://en.wikipedia.org/wiki/Null_(SQL)#Comparisons_with_NULL_and_the_three-valued_logic_.283VL.29) here, but doesn't comparison with a NULL value in SQL return Unknown, not False? And isn't the negation of Unknown still Unknown? So if SQL logic was coded in C++, wouldn't you want `NULL == something` to return Unknown, and you'd also want `NULL != something` to return Unknown, and you'd want `!Unknown` to return `Unknown`. And in that case implementing `operator!=` as the negation of `operator==` is still correct. – Benjamin Lindley Jun 14 '16 at 15:58
  • @BenjaminLindley The context being discussed is an overloading that returns a boolean. C doesn't have a boolean `Unknown`, so the idea that they could return a value that is its own inverse doesn't really compute. – Barmar Jun 14 '16 at 18:36
  • 1
    @Barmar: Okay, but then how does that make the statement *"SQL NULLs work this way"* correct? If we're restricting our comparison operator implementations to returning booleans, doesn't that just mean that implementing SQL logic with these operators is impossible? – Benjamin Lindley Jun 14 '16 at 19:24
  • @BenjaminLindley When used in a conditional context, `unknown` is effectively the same as `false`. So `if a = null` and `if a != null` both fail. – Barmar Jun 14 '16 at 19:27
  • 1
    @Barmar: I contend that any library which implements that logic is terribly designed. And if someone truly wanted to implement this SQL logic via C++ operator overloading, they would use a three valued logic, just like SQL does. – Benjamin Lindley Jun 14 '16 at 19:36
  • @BenjaminLindley The point is that the language doesn't restrict you to using `==` and `!=` only for objects that have simple binary logic. – Barmar Jun 14 '16 at 19:38
  • 2
    @Barmar: Well no, that's not the point. The OP already knows that fact, or this question wouldn't exist. The point was to present an example where it made sense either 1) to implement one of `operator==` or `operator!=`, but not the other, or 2) to implement `operator!=` in a way other than the negation of `operator==`. And implementing SQL logic for NULL values is not a case of that. – Benjamin Lindley Jun 14 '16 at 19:44
  • @BenjaminLindley I'm glad that you understood my question so well. Can you suggest any improvements to my question so that others could understand it better as well and to get rid of any confusion which might arise? – BarbaraKwarc Jun 14 '16 at 21:50
  • 2
    @dan1111 in my experience with sql server and bigquery, `X == null` and `X != null` most certainly evaluate to `null`, not `false`. How can I tell, you may ask? a) these values display as `null`, not `false` b) `not (X == null)` and `not (X != null)` don't evaluate to `true`, is a lesson every sql programmer learns at some point.. Indeed, I believe all major sql implementations adhere very closely to (some iteration of) the sql standard. – oulenz Jun 15 '16 at 14:05
  • @oulenz, wow, I didn't know that! Thanks for the correction. –  Jun 15 '16 at 15:11
  • Thanks indeed, everyone! I've corrected my answer. – Jander Jun 16 '16 at 04:12
24

I will only answer the second part of your question, namely:

If there is no such possibility, then why on Earth does C++ have these two operators being defined as two distinct functions?

One reason why it makes sense to allow the developer to overload both is performance. You might allow optimizations by implementing both == and !=. Then x != y might be cheaper than !(x == y) is. Some compilers may be able to optimize it for you, but perhaps not, especially if you have complex objects with a lot of branching involved.

Even in Haskell, where developers take laws and mathematical concepts very seriously, one is still allowed to overload both == and /=, as you can see here (http://hackage.haskell.org/package/base-4.9.0.0/docs/Prelude.html#v:-61--61-):

$ ghci
GHCi, version 7.10.2: http://www.haskell.org/ghc/  :? for help
λ> :i Eq
class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool
        -- Defined in `GHC.Classes'

This would probably be considered micro-optimization, but it might be warranted for some cases.

Centril
  • 2,224
  • 17
  • 28
  • 3
    SSE (x86 SIMD) wrapper classes are a great example of this. There's a [`pcmpeqb`](http://www.felixcloutier.com/x86/PCMPEQB:PCMPEQW:PCMPEQD.html) instruction, but no packed-compare instruction producing a != mask. So if you can't just reverse the logic of whatever uses the results, you have to use another instruction to invert it. (Fun fact: AMD's XOP instruction-set does have packed-compare for `neq`. Too bad Intel didn't adopt/extend XOP; there are some useful instructions in that soon-to-be-dead ISA extension.) – Peter Cordes Jun 15 '16 at 14:28
  • 1
    The whole point of SIMD in the first place is performance, and you typically only bother to use it manually in loops that are important for overall perf. Saving a single instruction (`PXOR` with all-ones to invert the compare mask result) in a tight loop can matter. – Peter Cordes Jun 15 '16 at 14:31
  • Performance as a reason is not credible when the overhead is *one logical negation*. – Cheers and hth. - Alf Jun 19 '16 at 04:08
  • It might be more than one logical negation if computing `x == y` costs more significantly more than `x != y`. Computing the latter might be significantly cheaper due to branch prediction, etc. – Centril Jun 19 '16 at 04:11
16

Is there any situation possible in which asking questions about two objects being equal does make sense, but asking about them not being equal doesn't make sense? (either from the user's perspective, or the implementer's perspective)

That's an opinion. Maybe it doesn't. But the language designers, not being omniscient, decided not to restrict people who might come up with situations in which it might make sense (at least to them).

Benjamin Lindley
  • 95,516
  • 8
  • 172
  • 256
13

In response to the edit;

That is, if it is possible for some type to have the operator == but not the !=, or vice versa, and when does it make sense to do so.

In general, no, it doesn't make sense. Equality and relational operators generally come in sets. If there is the equality, then the inequality as well; less than, then greater than and so on with the <= etc. A similar approach is applied to the arithmetic operators as well, they also generally come in natural logical sets.

This is evidenced in the std::rel_ops namespace. If you implement the equality and less than operators, using that namespace gives you the others, implemented in terms of your original implemented operators.

That all said, are there conditions or situations where the one would not immediately mean the other, or could not be implemented in terms of the others? Yes there are, arguably few, but they are there; again, as evidenced in the rel_ops being a namespace of its own. For that reason, allowing them to be implemented independently allows you to leverage the language to get the semantics you require or need in a way that is still natural and intuitive for the user or client of the code.

The lazy evaluation already mentioned is an excellent example of this. Another good example is giving them semantics that don't mean equality or in-equality at all. A similar example to this is the bit shift operators << and >> being used for stream insertion and extraction. Although it may be frowned upon in general circles, in some domain specific areas it may make sense.

Niall
  • 28,102
  • 9
  • 90
  • 124
12

If the == and != operators don't actually imply equality, in the same way that the << and >> stream operators don't imply bit-shifting. If you treat the symbols as if they mean some other concept, they don't have to be mutually exclusive.

In terms of equality, it could make sense if your use-case warrants treating objects as non-comparable, so that every comparison should return false (or a non-comparable result type, if your operators return non-bool). I can't think of a specific situation where this would be warranted, but I could see it being reasonable enough.

Taywee
  • 1,243
  • 11
  • 15
7

With great power comes great responsibly, or at least really good style guides.

== and != can be overloaded to do whatever the heck you want. It's both a blessing and a curse. There's no guarantee that != means !(a==b).

dippas
  • 49,171
  • 15
  • 93
  • 105
It'sPete
  • 4,728
  • 5
  • 33
  • 64
6
enum BoolPlus {
    kFalse = 0,
    kTrue = 1,
    kFileNotFound = -1
}

BoolPlus operator==(File& other);
BoolPlus operator!=(File& other);

I can't justify this operator overloading, but in the example above it is impossible to define operator!= as the "opposite" of operator==.

Dafang Cao
  • 830
  • 4
  • 14
  • 9
    [Why would you _ever_ define an enum like that?](http://thedailywtf.com/articles/What_Is_Truth_0x3f_) –  Jun 14 '16 at 01:40
  • 1
    @Snowman: Dafang doesn't say its a good enumeration (nor a good idea to define an enumeration like that), it is just an example to illustrate a point. With this (perhaps bad) operator definition, then `!=` would indeed not mean the opposite of `==`. – AlainD Jun 14 '16 at 11:17
  • 1
    @AlainD did you click the link I posted, and are you aware of the purpose of that site? This is called "humor." –  Jun 14 '16 at 14:47
  • 1
    @Snowman: I certainly do...sorry, I missed it was a link and intended as irony! :o) – AlainD Jun 15 '16 at 09:21
5

In the end, what you are checking with those operators is that the expression a == b or a != b is returning a Boolean value (true or false). These expression returns a Boolean value after comparison rather than being mutually exclusive.

Azeem
  • 7,094
  • 4
  • 19
  • 32
4

[..] why are there two separate definitions needed?

One thing to consider is that there might be the possibility of implementing one of these operators more efficiently than just using the negation of the other.

(My example here was rubbish, but the point still stands, think of bloom filters, for example: They allow fast testing if something is not in a set, but testing if it's in may take a lot more time.)

[..] by definition, a != b is !(a == b).

And it's your responsibility as programmer to make that hold. Probably a good thing to write a test for.

Daniel Jour
  • 15,219
  • 2
  • 27
  • 57
  • 4
    How does `!((a == rhs.a) && (b == rhs.b))` not allow short circuiting? if `!(a == rhs.a)`, then `(b == rhs.b)` will not be evaluated. – Benjamin Lindley Jun 13 '16 at 22:34
  • This is a bad example, though. The short-circuiting adds no magical advantage here. – Oliver Charlesworth Jun 13 '16 at 22:34
  • @Oliver Charlesworth Alone it doesn't, but when joined with separate operators, it does: In case of `==`, it will stop comparing as soon as the first corresponding elements are non-equal. But in case of `!=`, if it were implemented in terms of `==`, it would need to compare all the corresponding elements first (when they're all equal) to be able to tell that they are not non-equal :P But when implemented as in the example above, it will stop comparing as soon as it finds the first non-equal pair. Great example indeed. – BarbaraKwarc Jun 13 '16 at 22:53
  • @BenjaminLindley True, my example was complete nonsense. Unfortunately, I cannot come up with another atm, it's too late here. – Daniel Jour Jun 13 '16 at 22:54
  • 1
    @BarbaraKwarc: `!((a == b) && (c == d))` and `(a != b) || (c != d)` are equivalent in terms of short-circuiting efficiency. – Oliver Charlesworth Jun 13 '16 at 23:01
  • The bloom filters example is also bad. Set membership is not the same as equality. I don't think there is any example of a performance gain by implementing them separately (for standard meanings of the operators). Because as soon as you find the answer to whether something is equal, you also know whether it is not equal. –  Jun 14 '16 at 08:03
  • It's impossible for `!=` to be more efficient than `==` (Assuming `a == b` == `!(a != b)`. Because if it was, you could just implement `==` the same way as `!=`, but with the output inverted. – 12Me21 Oct 25 '18 at 23:33
2

Maybe an uncomparable rule, where a != b was false and a == b was false like a stateless bit.

if( !(a == b || a != b) ){
    // Stateless
}
Azeem
  • 7,094
  • 4
  • 19
  • 32
ToñitoG
  • 119
  • 1
  • 6
  • If you want to rearrange logical symbols then !( [A] || [B]) logically becomes ([!A]&[!B]) – Thijser Jun 14 '16 at 19:35
  • Note that the return type of `operator==()` and `operator!=()` are not necessarily `bool`, they might be an enum that include stateless if you wanted that and yet the operators might still be defined so `(a != b) == !(a==b)` holds.. – lorro Jul 26 '16 at 16:54
2

By customizing the behavior of the operators, you can make them do what you want.

You may wish to customize things. For instance, you may wish to customize a class. Objects of this class can be compared just by checking a specific property. Knowing that this is the case, you can write some specific code that only checks the minimum things, instead of checking every single bit of every single property in the whole object.

Imagine a case where you can figure out that something is different just as fast, if not faster, than you can find out something is the same. Granted, once you figure out whether something is the same or different, then you can know the opposite simply by flipping a bit. However, flipping that bit is an extra operation. In some cases, when code gets re-executed a lot, saving one operation (multiplied by many times) can have an overall speed increase. (For instance, if you save one operation per pixel of a megapixel screen, then you've just saved a million operations. Multiplied by 60 screens per second, and you save even more operations.)

hvd's answer provides some additional examples.

TOOGAM
  • 131
  • 2
  • 8
2

Yes, because one means "equivalent" and another means "non-equivalent" and this terms are mutually exclusive. Any other meaning for this operators is confusing and should be avoided by all means.

oliora
  • 553
  • 2
  • 11
  • They are not mutually exclusive for _all_ cases. For example, two infinities both not equal to each other and not not equal to each other. – vladon Jun 30 '16 at 14:22
  • @vladon can use use one instead other in *generic case*? No. This means they are just not equal. All the rest goes to a special function rather than to operator==/!= – oliora Jun 30 '16 at 15:35
  • @vladon please, instead of *generic case* read *all cases* in my answer. – oliora Jun 30 '16 at 15:50
  • @vladon As much as this is true in maths, can you give an example where `a != b` is not equal to `!(a == b)` for this reason in C? – nitro2k01 May 18 '18 at 15:36