digitalmars.D.learn - Which operators cannot be overloaded and why not?
- NonNull (1/1) Sep 13 2021 Which operators cannot be overloaded and why not?
- user1234 (19/20) Sep 13 2021 Let's start the list.
- jfondren (7/8) Sep 13 2021 And many other boolean operators, unary !, binary && and ||
- Paul Backus (3/11) Sep 13 2021 I think the intent is for the boolean operators to be handled via
- user1234 (3/16) Sep 13 2021 I didn't see it was already suggested and answered the same...
- Basile B. (24/32) Sep 13 2021 Oh! I have never noticed that `&&` and `||`, despite of being
- user1234 (14/17) Sep 13 2021 They are all indirectly supported if `opCast` is overloaded:
- user1234 (2/4) Sep 13 2021 i.e "prevent the semantics to be hijacked".
- NonNull (5/12) Sep 13 2021 Is there no list of such in an article online? It would be good
- user1234 (4/18) Sep 13 2021 well this whole thread is certainly the raw material for an
- user1234 (17/18) Sep 13 2021 when you have
- Steven Schveighoffer (12/35) Sep 13 2021 This is because the compiler calls a different hook depending on the
- H. S. Teoh (59/60) Sep 13 2021 Others have already given the list, so I won't repeat that. As to the
- NonNull (4/12) Sep 13 2021 Thanks for this explanation. The consolidation you mention is
- H. S. Teoh (69/77) Sep 14 2021 [...]
Which operators cannot be overloaded and why not?
Sep 13 2021
On Monday, 13 September 2021 at 14:12:36 UTC, NonNull wrote:Which operators cannot be overloaded and why not?Let's start the list. - `new` and `delete` Because operators are overloaded in aggregates new was supported but not anymore, the idea is that a an aggregate should not be tied a specific allocator. - post incr/decr See [https://dlang.org/spec/operatoroverloading.html#postincrement_postdecrement_operat rs](explantations). This is an arbitrary limitation imho but the current way enfore that the side effect is not apllied to the expression result. - condition al expression ` cond ? exp : exp ` No idea why it is not supported... nobody has never complained tho ;) - `typeid` Same... the result of typeid has to be a TypeInfoClass, so there's not much to do... exactness is expected - `is` (identity) could be but I suppose that this stuff needs to be correct to keep code consistent what else ?
Sep 13 2021
On Monday, 13 September 2021 at 14:33:03 UTC, user1234 wrote:- condition al expression ` cond ? exp : exp `And many other boolean operators, unary !, binary && and || https://dlang.org/spec/operatoroverloading.html lists all the overloadable operators, and https://dlang.org/spec/expression.html has all the operators, so "which operators" is a matter of close comparison. "why not" is much harder to answer.
Sep 13 2021
On Monday, 13 September 2021 at 14:42:42 UTC, jfondren wrote:On Monday, 13 September 2021 at 14:33:03 UTC, user1234 wrote:I think the intent is for the boolean operators to be handled via `opCast!bool`.- condition al expression ` cond ? exp : exp `And many other boolean operators, unary !, binary && and || https://dlang.org/spec/operatoroverloading.html lists all the overloadable operators, and https://dlang.org/spec/expression.html has all the operators, so "which operators" is a matter of close comparison. "why not" is much harder to answer.
Sep 13 2021
On Monday, 13 September 2021 at 14:59:38 UTC, Paul Backus wrote:On Monday, 13 September 2021 at 14:42:42 UTC, jfondren wrote:I didn't see it was already suggested and answered the same... but yet that's exactly the case.On Monday, 13 September 2021 at 14:33:03 UTC, user1234 wrote:I think the intent is for the boolean operators to be handled via `opCast!bool`.- condition al expression ` cond ? exp : exp `And many other boolean operators, unary !, binary && and || https://dlang.org/spec/operatoroverloading.html lists all the overloadable operators, and https://dlang.org/spec/expression.html has all the operators, so "which operators" is a matter of close comparison. "why not" is much harder to answer.
Sep 13 2021
On Monday, 13 September 2021 at 14:42:42 UTC, jfondren wrote:On Monday, 13 September 2021 at 14:33:03 UTC, user1234 wrote:Oh! I have never noticed that `&&` and `||`, despite of being quite "ordinary" binary ops are not overloadable. In [styx](https://styx-lang.gitlab.io/styx/attribute.html#operatorattribute) that works because, although inspired by the D way, in the sense that overloads are implemented in custom types, the selection is done using an expression template ```d struct S { operator(a && b) function andAnd(T other): auto {return false} } ``` So as long as the expression in the attribute argument looks like a valid expression the stuff is found (and the supprot code in the compiler is super simple), e.g with code in a body: ```d e1 && e2; // look if e1 is an aggregate and if it contains operator(a && b) e1 + e2; // look if e1 is an aggregate and if it contains operator(a && b) ``` While D uses specific identifier + templates value param for strings. (note that only the equivalent of D opBinary**Right** works...) anyway. zorry for this off-topic.- condition al expression ` cond ? exp : exp `And many other boolean operators, unary !, binary && and || https://dlang.org/spec/operatoroverloading.html lists all the overloadable operators, and https://dlang.org/spec/expression.html has all the operators, so "which operators" is a matter of close comparison. "why not" is much harder to answer.
Sep 13 2021
On Monday, 13 September 2021 at 14:42:42 UTC, jfondren wrote:On Monday, 13 September 2021 at 14:33:03 UTC, user1234 wrote:They are all indirectly supported if `opCast` is overloaded: ```d unittest { struct S { bool opCast(T = bool)(){return true;} } S s; assert(s && s); } ``` so this is a bit like the postincr case.- condition al expression ` cond ? exp : exp `And many other boolean operators, unary !, binary && and ||
Sep 13 2021
On Monday, 13 September 2021 at 15:29:05 UTC, user1234 wrote:[...] so this is a bit like the postincr case.i.e "prevent the semantics to be hijacked".
Sep 13 2021
On Monday, 13 September 2021 at 14:42:42 UTC, jfondren wrote:On Monday, 13 September 2021 at 14:33:03 UTC, user1234 wrote:Is there no list of such in an article online? It would be good if that work was done for once and for all, with a short explanation in each case, possibly with discussion in some cases of how to achieve results by other means.- condition al expression ` cond ? exp : exp `And many other boolean operators, unary !, binary && and || https://dlang.org/spec/operatoroverloading.html lists all the overloadable operators, and https://dlang.org/spec/expression.html has all the operators, so "which operators" is a matter of close comparison.
Sep 13 2021
On Monday, 13 September 2021 at 18:06:42 UTC, NonNull wrote:On Monday, 13 September 2021 at 14:42:42 UTC, jfondren wrote:well this whole thread is certainly the raw material for an article (or D blog post) on D operator overloads. Just, someone has to compile the informations and write the said article.On Monday, 13 September 2021 at 14:33:03 UTC, user1234 wrote:Is there no list of such in an article online? It would be good if that work was done for once and for all, with a short explanation in each case, possibly with discussion in some cases of how to achieve results by other means.- condition al expression ` cond ? exp : exp `And many other boolean operators, unary !, binary && and || https://dlang.org/spec/operatoroverloading.html lists all the overloadable operators, and https://dlang.org/spec/expression.html has all the operators, so "which operators" is a matter of close comparison.
Sep 13 2021
On Monday, 13 September 2021 at 14:33:03 UTC, user1234 wrote:what else ?when you have ```d alias AA1 = int[int]; alias AA2 = AA1[int]; ``` then you can write ```d AA2 aa; aa[0] = [0 : 0]; aa[0][0] = 0; ``` The `[0][0]` cannot be expressed using operator overloads (and a custom map type that implements opIndexAssign).But this case is rather due to the fact that druntime seems to do something a bit unusal here (according to an old discussion that happend once on irc).
Sep 13 2021
On 9/13/21 10:47 AM, user1234 wrote:On Monday, 13 September 2021 at 14:33:03 UTC, user1234 wrote:This is because the compiler calls a different hook depending on the usage of the expression `aa[0]`. Which one it calls is not consistent. There isn't an analog for indexing overloads. A further example: ```d int[int] aa; aa[0]++; // ok void foo(ref int x) {x++;} foo(aa[1]); // range violation ``` -Stevewhat else ?when you have ```d alias AA1 = int[int]; alias AA2 = AA1[int]; ``` then you can write ```d AA2 aa; aa[0] = [0 : 0]; aa[0][0] = 0; ``` The `[0][0]` cannot be expressed using operator overloads (and a custom map type that implements opIndexAssign).But this case is rather due to the fact that druntime seems to do something a bit unusal here (according to an old discussion that happend once on irc).
Sep 13 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
Sep 13 2021
On Monday, 13 September 2021 at 16:12:34 UTC, H. S. Teoh wrote:On Mon, Sep 13, 2021 at 02:12:36PM +0000, NonNull via Digitalmars-d-learn wrote:I didn't see unary &. Maybe others are missing.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.Thanks for this explanation. The consolidation you mention is great!
Sep 13 2021
On Mon, Sep 13, 2021 at 06:19:20PM +0000, NonNull via Digitalmars-d-learn wrote:On Monday, 13 September 2021 at 16:12:34 UTC, H. S. Teoh wrote:[...] IIRC, unary & cannot be overloaded for the same reasons &&, ||, and ! cannot be overloaded: it leads to hard-to-understand, hard-to-maintain code. Incidentally, C++ *does* allow overloading of &, ostensibly because some wrapper types might want to use it to become transparent. But one of the consequences of this is that now you need a std::address-of pseudo-operator that *actually* takes an address of something when you *really* mean to take the address of the wrapper object instead of the wrapped object. So & loses its original meaning and has to be replaced by a std:: hack. Which, to me, is abundant proof that overloading & was a bad idea in the first place. // BTW, as an aside, as a example of why every-man-for-himself, wild-wild-west operator overloading in C++ is a bad idea, consider these two lines of C++ code: fun<A, B>(a, b); gun<T, U>(a, b); What do they mean? Ostensibly, these are function calls to two template functions, specifying explicit template arguments. Unfortunately, that is not the case: only *one* of these lines is a template function call, the other is something else altogether. But nobody can tell the difference unless they read the entire context, as shown below. (Incidentally, this example also shows why C++'s choice of template syntax was a poor one.) Compile and run with a C++ compiler to find out what the actual behaviour is. ------- // Totally evil example of why C++ template syntax and free-for-all // operator overloading is a Bad, Bad Idea. #include <iostream> struct Bad { }; struct B { }; struct A { Bad operator,(B b) { return Bad(); } }; struct D { }; struct Ugly { D operator>(Bad b) { return D(); } } U; struct Terrible { } T; struct Evil { ~Evil() { std::cout << "Hard drive reformatted." << std::endl; } }; struct Nasty { Evil operator,(D d) { return Evil(); } }; struct Idea { void operator()(A a, B b) { std::cout << "Good idea, data saved." << std::endl; } Nasty operator<(Terrible t) { return Nasty(); } } gun; template<typename T, typename U> void fun(A a, B b) { std::cout << "Have fun!" << std::endl; } int main() { A a; B b; // What do these lines do? fun<A, B>(a, b); gun<T, U>(a, b); } ------- T -- Your inconsistency is the only consistent thing about you! -- KDOn Mon, Sep 13, 2021 at 02:12:36PM +0000, NonNull via Digitalmars-d-learn wrote:I didn't see unary &. Maybe others are missing.Which operators cannot be overloaded and why not?Others have already given the list, so I won't repeat that.
Sep 14 2021