View mode: basic / threaded / horizontal-split · Log in · Help
July 20, 2012
Semantics of postfix ops for classes
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.
July 25, 2012
Re: Semantics of postfix ops for classes
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;
July 25, 2012
Re: Semantics of postfix ops for classes
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.
Top | Discussion index | About this forum | D home