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