Some problems with operator overloading

Steven Schveighoffer schveiguy at yahoo.com
Mon Mar 15 06:20:44 PDT 2010


On Sun, 14 Mar 2010 10:55:41 -0400, bearophile <bearophileHUGS at lycos.com>  
wrote:

> In the following little programs can you help me tell apart my errors  
> from bugs in dmd?

I'll preface my comments with a general comment.

operator functions themselves are not, and should not be, treated  
specially by the compiler.  You can write opBinary(int x) for all the  
compiler cares, and it's a valid function.  opX is not a keyword or  
specially treated symbol.

The way D works (and has always worked, even in D1) is that the operators  
themselves (e.g. '+' '-', etc.) are transformed into a function call.

The benefit to all this is that keyword pollution is kept to a minimum,  
and the compiler is extremely simplified.  Imagine if opBinary is treated  
as a keyword, you need a special cases littered throughout the code,  
special grammar, etc.  If opBinary is just another symbol, it's already  
covered by the compiler.  The only place it's special int the compiler is  
when it's *generating* opBinary, not when it's *parsing* opBinary.

>
> 1) Error in the documentation in this page:
> http://www.digitalmars.com/d/2.0/operatoroverloading.html
>
> This:
>
> struct S {
>     int opEquals(ref const S s) { ... }
> }
>
> Probably has to become like:
>
> struct S {
>     const bool opEquals(ref const(S) s) { ... }
> }

I think there is no restriction on what opEquals in structs.  However,  
where opEquals is a virtual function, a const-correct signature must be  
required in order to override the base function.  I filed a recent bug on  
it, where in fact you can change an object through a const reference  
because the compiler forces it.

>
> -------------------------------
>
> 2) This code runs:
>
>
> import std.c.stdio: printf;
> struct Foo {
>     int data;
>     int opEquals(T:Foo)(T other) {
>         printf("A");
>         return this.data == other.data;
>     }
> }
> void main() {
>     int r = Foo(5) == Foo(5);
> }
>
>
> But I think dmd has to raise a c error, and require something like:
> const bool opEquals(T:Foo)(ref const(Foo) other) {
> Or:
> const bool opEquals(T:Foo)(const(Foo) other) {
> etc.

Again, opEquals is not a special function.  Comparing two const Foos will  
correctly result in a compiler error.

>
> -------------------------------
>
> 3) This code runs:
>
>
> struct Foo {
>     int data;
>     bool opBinary(string Op:"==")(ref const Foo other) { // wrong:  
> opEquals
>         return this.data == other.data;
>     }
>     bool opBinary(string Op)(ref const Foo other) if (Op == "0") { //  
> wrong
>         return this.data == other.data;
>     }
> }
> void main() {}
>
>
> But that can cause bugs: it's easy for programmers to forget to use  
> opEquals instead of opBinary("==").

Again, opBinary and opEquals are simply functions.  There is nothing  
special about their existence, just about how the compiler rewrites the  
operators into calling those functions.  The fact that opBinary does not  
get called is the same as defining a function that does not get called.   
These kinds of errors are appropriate for a lint tool.

> And generally I'd like dmd to raise an error when silly ops are defined,  
> like that "0". It's important to avoid many bugs in the operator  
> overloading usage.

Note that opBinary can be directly called via:

myfoo.opBinary!("0")(other);

>
> -------------------------------
>
> 4) This program doesn't compile:
>
>
> struct Foo {
>     int x;
>     Foo opUnary(string op:"++")() {
>         this.x++;
>         return this;
>     }
> }
> void main() {
>     Foo f = Foo(5);
>     f++; // line 10
> }
>
>
> Prints:
> temp.d(10): Error: var has no effect in expression (__tmp1)

This is a bug, I think introduced by a recent change that makes f++ an  
lvalue.  Please file a bugzilla report.

> -------------------------------
>
> 5) Here an implicit cast to uint or int can be handy, to avoid the cast  
> (to be able to create a generic "fake int" struct), is this possible?
>
>
> struct Foo {
>     int x;
>     int opCast(T:int)() {
>         return this.x;
>     }
> }
> void main() {
>     Foo f = Foo(5);
>     auto a1 = new int[cast(int)f]; // OK
>     auto a2 = new int[f];          // ERR
> }
>
>
> Philippe Sigaud in the digitalmars.D.learn newsgroup suggests an  
> opImplicitCast.

opImplicitCast was abandoned for the more versatile alias this.  Something  
like this should work:

struct Foo {
   int x;
   int opCast(T:int)() { return this.x; };
   alias opCast this;
}

-Steve



More information about the Digitalmars-d mailing list