www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Semantics of postfix ops for classes

reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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
parent reply Don Clugston <dac nospam.com> writes:
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
parent travert phare.normalesup.org (Christophe Travert) writes:
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