ALL operator overloading should do "what you expect". There is no point in having an operator == that returns true if the objects aren't substantially the same. How you define "the same" is of course potentially something we could argue about. One could implement a "nocasestring" where it behaves like a string, but if you have a string "HELLO" and one "hello", they are considered equal, because case doesn't matter. Or if you implemented your own "Float" class, where == does some math to avoid the fragile comparison of regular floating point. So yes, there are cases where == doesn't do EXACTLY "for all elements is a. == b.", but it should really be the exception.
Likewise, if an operator doesn't make totally sense, don't make it do something "surprising" (e.g. -
for strings - what does it do? Or multiply one string by another string - multiply by integer may have a meaning).
Surprises in programming is a bad idea. If things don't work the way you EXPECT, you get bad experiences from reading/modifying the code.