digitalmars.D - Semantics of postfix ops for classes
- Andrej Mitrovic (38/38) Jul 20 2012 According to TDPL postfix operators are rewritten to call prefix
- Don Clugston (12/50) Jul 25 2012 But classes have reference semantics, so they are already completely
- travert phare.normalesup.org (Christophe Travert) (3/13) Jul 25 2012 Similarly, the langage should provide a way to disable postfix++ on
According to TDPL postfix operators are rewritten to call prefix operators, e.g. on this call for some user-type object named a: auto b = a++; // is converted to: auto b = ((ref x) { auto t = x; ++x; return t; })(a); But I don't see how this is reasonable for classes. Examine: struct Struct { int x = 1; Struct opUnary(string op : "++")() { x++; return this; } } class Class { int x = 1; Class opUnary(string op : "++")() { x++; return this; } } void main() { Struct foo1; Struct foo2 = foo1++; assert(foo1.x != foo2.x); // ok Class bar1 = new Class; Class bar2 = bar1++; assert(bar1.x != bar2.x); // fail } It's clear why, the rewrite that calls "auto t = x" simply binds another reference to the same object. Unfortunately this makes it hard to wrap C++ libraries which have both prefix/postfix operators defined. Currently I wrap these in e.g. "preInc"/"postInc" methods and I explicitly disable the prefix/postfix opUnary methods. Are the semantics of this rewrite ok with people who use op overloads? I found them to be surprising, but then again I don't use op overloads that much, I'm just noticing the difference between C++ and D.
Jul 20 2012
On 20/07/12 17:12, Andrej Mitrovic wrote:According to TDPL postfix operators are rewritten to call prefix operators, e.g. on this call for some user-type object named a: auto b = a++; // is converted to: auto b = ((ref x) { auto t = x; ++x; return t; })(a); But I don't see how this is reasonable for classes. Examine: struct Struct { int x = 1; Struct opUnary(string op : "++")() { x++; return this; } } class Class { int x = 1; Class opUnary(string op : "++")() { x++; return this; } } void main() { Struct foo1; Struct foo2 = foo1++; assert(foo1.x != foo2.x); // ok Class bar1 = new Class; Class bar2 = bar1++; assert(bar1.x != bar2.x); // fail } It's clear why, the rewrite that calls "auto t = x" simply binds another reference to the same object. Unfortunately this makes it hard to wrap C++ libraries which have both prefix/postfix operators defined. Currently I wrap these in e.g. "preInc"/"postInc" methods and I explicitly disable the prefix/postfix opUnary methods. Are the semantics of this rewrite ok with people who use op overloads? I found them to be surprising, but then again I don't use op overloads that much, I'm just noticing the difference between C++ and D.But classes have reference semantics, so they are already completely different from C++. The question really is, do postfix ++ and -- make sense for reference types? Arguably not. From a theoretical sense, the existing behaviour does make sense, but in practice, every time it is used, it is probably a bug. The only other reasonable option I can think of would be to make class++ be of type void, so that you could still write bar1++; but not bar2 = bar1++; since the existing behaviour can be achieved by writing bar2 = ++ bar1;
Jul 25 2012
Don Clugston , dans le message (digitalmars.D:173192), a écrit :The question really is, do postfix ++ and -- make sense for reference types? Arguably not. From a theoretical sense, the existing behaviour does make sense, but in practice, every time it is used, it is probably a bug. The only other reasonable option I can think of would be to make class++ be of type void, so that you could still write bar1++; but not bar2 = bar1++; since the existing behaviour can be achieved by writing bar2 = ++ bar1;Similarly, the langage should provide a way to disable postfix++ on a struct, since a struct can be a reference type.
Jul 25 2012