Which operators cannot be overloaded and why not?

H. S. Teoh hsteoh at quickfur.ath.cx
Mon Sep 13 16:12:34 UTC 2021


On Mon, Sep 13, 2021 at 02:12:36PM +0000, NonNull via Digitalmars-d-learn wrote:
> Which operators cannot be overloaded and why not?

Others have already given the list, so I won't repeat that. As to the
"why":

In general, D tries to avoid the wild wild west, every operator for
himself situation in C++ that leads to unreadable, unmaintainable code.

For example, C++ allows you to separately overload <, <=, ==, >, >=, !=.
That means the programmer is free to define == and != in a way
completely inconsistent with each other, for example.  D solves this by
combining == and != into a single operator overload, opEquals, where !=
is the negation of ==. This ensures == and != are always consistent with
each other.  Same with <, <=, >, >=: C++ lets you overload each one
separately, potentially in a completely inconsistent way with each
other, so that somebody reading your code has no idea whether x < y
implies y >= x, or whether x < y implies y > x. Again, unreadable code.
D solves this by combining all these operators into a single overload:
opCmp.  This ensures consistency between all of these relative
comparison operators.

Similarly, prefix ++/-- and postfix ++/-- cannot be separately
overloaded; they are combined into a single overload, where postfix
++/--, as in `x++`, is simply rewritten by the compiler to the
equivalent of:

	(){ auto tmp = x; x++; return tmp; }()

(Of course, this is just to illustrate the semantics. The compiler
doesn't actually create a lambda here.)

This ensures that prefix and postfix operators behave in the expected
way, so that ++x and x++ don't behave in two wildly different, unrelated
ways, like it sometimes happens in C++ code because C++ lets you define
separate overloads for them.

Combining these operator overloads have the additional benefit that less
overloads are needed to implement a numerical type: instead of needing
to separately implement ==, !=, <, <=, >, >=, you only have to implement
two overloads, opEquals and opCmp, and the compiler takes care of the
rest. Similarly, ++ and -- only need to be implement once each, and
you'll get the postfix varieties for free.

Furthermore, the boolean operators !, &&, || are not directly
overloadable. Again, C++ lets you separately overload them, which
guarantees that when you see (what looks like) a boolean expression in
C++, you have no idea what its true semantics are, because && could mean
something completely different from the usual meaning.  D doesn't let
you overload these operators, thereby ensuring that a boolean expression
remains a boolean expression: !, &&, || always have the standard
semantics.  What D *does* let you do is to define opCast!bool yourself,
so that you can define how a user-defined type converts to a bool, which
can then participate in the !, &&, || operators in the usual way.


In general, the philosophy in D is that operator overloading really
should be reserved for number-like (or math-like) objects, and
overloaded operators are expected to behave more-or-less like their
standard, non-overloaded meanings. Operator overloading of the variety
that C++ likes to do, like iostream's <<, >> overloads, are frowned
upon, because they obscure the surface meaning of the code and make it
hard to understand and maintain. (Incidentally, D *does* allow you to
overload '<<' and '>>'. It's just frowned upon to overload them in a way
that doesn't in someway represent bit-shifting.)  Furthermore,
overloaded operators are expected to behave analogously w.r.t. each
other, e.g., == and != should behave like opposites of each other; <
should not have completely unrelated semantics to >, and so on.


T

-- 
The right half of the brain controls the left half of the body. This means that only left-handed people are in their right mind. -- Manoj Srivastava


More information about the Digitalmars-d-learn mailing list