689 changes: 625 additions & 64 deletions operatoroverloading.dd
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,623 @@ Ddoc

$(SPEC_S Operator Overloading,

$(V2
$(P Operator overloading is accomplished by rewriting operators whose
operands are class or struct objects into calls to specially named
member functions. No additional syntax is used.
)

$(UL
$(LI $(LINK2 #Unary, Unary Operator Overloading))
$(LI $(LINK2 #Cast, Cast Operator Overloading))
$(LI $(LINK2 #Binary, Binary Operator Overloading))
$(LI $(LINK2 #equals, Overloading == and !=))
$(LI $(LINK2 #compare, Overloading < <=, > and >=))
$(LI $(LINK2 #FunctionCall, Function Call Operator Overloading))
$(LI $(LINK2 #Assignment, Assignment Operator Overloading))
$(LI $(LINK2 #OpAssign, Op Assignment Operator Overloading))
$(LI $(LINK2 #Array, Index Operator Overloading))
$(LI $(LINK2 #Slice, Index Operator Overloading))
$(LI $(LINK2 #Dispatch, Forwarding))
)

<h2><a name="Unary">Unary Operator Overloading</a></h2>

$(TABLE2 Overloadable Unary Operators,

$(TR $(TH$(I op)) $(TH$(I rewrite)) )

$(TR
$(TD -$(I e))
$(TD $(CODE $(I e).opUnary!("-")()))
)

$(TR
$(TD +$(I e))
$(TD $(CODE $(I e).opUnary!("+")()))
)

$(TR
$(TD ~$(I e))
$(TD $(CODE $(I e).opUnary!("~")()))
)

$(TR
$(TD *$(I e))
$(TD $(CODE $(I e).opUnary!("*")()))
)

$(TR
$(TD ++$(I e))
$(TD $(CODE $(I e).opUnary!("++")()))
)

$(TR
$(TD --$(I e))
$(TD $(CODE $(I e).opUnary!("--")()))
)

)

$(P For example, in order to overload the - (negation) operator for struct S, and
no other operator:)

---
struct S {
int m;

int opUnary(string s)() if (s == "-") {
return -m;
}
}

int foo(S s) {
return -s;
}
---

<h3>Postincrement $(I e)++ and Postdecrement $(I e)-- Operators</h3>

$(P These are not directly overloadable, but instead are rewritten
in terms of the ++$(I e) and --$(I e) prefix operators:
)

$(TABLE2 Postfix Operator Rewrites,

$(TR $(TH$(I op)) $(TH$(I rewrite)) )

$(TR
$(TD $(I e)--)
$(TD $(CODE (auto t = e, --$(I e), t)))
)

$(TR
$(TD $(I e)++)
$(TD $(CODE (auto t = e, ++$(I e), t)))
)
)

<h3>Overloading Index Unary Operators</h3>

$(TABLE2 Overloadable Index Unary Operators,

$(TR $(TH$(I op)) $(TH$(I rewrite)) )

$(TR
$(TD $(CODE -$(I a)[$(ARGUMENTS)]))
$(TD $(CODE $(I a).opIndexUnary!("-")($(ARGUMENTS))))
)

$(TR
$(TD $(CODE +$(I a)[$(ARGUMENTS)]))
$(TD $(CODE $(I a).opIndexUnary!("+")($(ARGUMENTS))))
)

$(TR
$(TD $(CODE ~$(I a)[$(ARGUMENTS)]))
$(TD $(CODE $(I a).opIndexUnary!("~")($(ARGUMENTS))))
)

$(TR
$(TD $(CODE *$(I a)[$(ARGUMENTS)]))
$(TD $(CODE $(I a).opIndexUnary!("*")($(ARGUMENTS))))
)

$(TR
$(TD $(CODE ++$(I a)[$(ARGUMENTS)]))
$(TD $(CODE $(I a).opIndexUnary!("++")($(ARGUMENTS))))
)

$(TR
$(TD $(CODE --$(I a)[$(ARGUMENTS)]))
$(TD $(CODE $(I a).opIndexUnary!("--")($(ARGUMENTS))))
)

)

<h3>Overloading Slice Unary Operators</h3>

$(TABLE2 Overloadable Slice Unary Operators,

$(TR $(TH$(I op)) $(TH$(I rewrite)) )

$(TR
$(TD $(CODE -$(I a)[$(SLICE)]))
$(TD $(CODE $(I a).opSliceUnary!("-")($(SLICE2))))
)

$(TR
$(TD $(CODE +$(I a)[$(SLICE)]))
$(TD $(CODE $(I a).opSliceUnary!("+")($(SLICE2))))
)

$(TR
$(TD $(CODE ~$(I a)[$(SLICE)]))
$(TD $(CODE $(I a).opSliceUnary!("~")($(SLICE2))))
)

$(TR
$(TD $(CODE *$(I a)[$(SLICE)]))
$(TD $(CODE $(I a).opSliceUnary!("*")($(SLICE2))))
)

$(TR
$(TD $(CODE ++$(I a)[$(SLICE)]))
$(TD $(CODE $(I a).opSliceUnary!("++")($(SLICE2))))
)

$(TR
$(TD $(CODE --$(I a)[$(SLICE)]))
$(TD $(CODE $(I a).opSliceUnary!("--")($(SLICE2))))
)

$(TR
$(TD $(CODE -$(I a)[ ]))
$(TD $(CODE $(I a).opSliceUnary!("-")()))
)

$(TR
$(TD $(CODE +$(I a)[ ]))
$(TD $(CODE $(I a).opSliceUnary!("+")()))
)

$(TR
$(TD $(CODE ~$(I a)[ ]))
$(TD $(CODE $(I a).opSliceUnary!("~")()))
)

$(TR
$(TD $(CODE *$(I a)[ ]))
$(TD $(CODE $(I a).opSliceUnary!("*")()))
)

$(TR
$(TD $(CODE ++$(I a)[ ]))
$(TD $(CODE $(I a).opSliceUnary!("++")()))
)

$(TR
$(TD $(CODE --$(I a)[ ]))
$(TD $(CODE $(I a).opSliceUnary!("--")()))
)
)

<h2><a name="Cast">Cast Operator Overloading</a></h2>

$(TABLE2 Cast Operators,

$(TR $(TH$(I op)) $(TH$(I rewrite)) )

$(TR
$(TD cast($(I type))$(I e))
$(TD $(CODE $(I e).opCast!($(I type))()))
)

)


<h3>Boolean Operations</h3>

$(P Notably absent from the list of overloaded unary operators is the !
logical negation operator. More obscurely absent is a unary operator
to convert to a bool result.
Instead, these are covered by a rewrite to:
)
---
opCast!(bool)(e)
---

$(P So,)

---
if (e) => if (e.opCast!(bool))
if (!e) => if (!e.opCast!(bool))
---

$(P etc., whenever a bool result is expected. This only happens, however, for
instances of structs. Class references are converted to bool by checking to
see if the class reference is null or not.
)

<h2><a name="Binary">Binary Operator Overloading</a></h2>

$(P The following binary operators are overloadable:)

$(TABLE2 Overloadable Binary Operators,

$(TR $(TD +) $(TD -) $(TD *) $(TD /) $(TD %) $(TD ^^) $(TD &amp;) )
$(TR $(TD |) $(TD ^) $(TD &lt;&lt;) $(TD &gt;&gt;) $(TD &gt;&gt;&gt;) $(TD ~) $(TD in) )

)

$(P The expression:)
---
a $(I op) b
---
$(P is rewritten as both:)
---
a.opBinary!("$(I op)")(b)
b.opBinaryRight!("$(I op)")(a)
---

$(P and the one with the 'better' match is selected.
It is an error for both to equally match.
)
)

<h2><a name="equals">Overloading == and !=</a></h2>

$(P Expressions of the form $(CODE a != b) are rewritten as $(CODE !(a == b)).)

$(P Given $(CODE a == b) :)

$(OL
$(LI If a and b are both class objects, then the expression is rewritten as:
---
.object.opEquals(a, b)
---
and that function is implemented as:
---
bool opEquals(Object a, Object b) {
if (a is b) return true;
if (a is null || b is null) return false;
if (typeid(a) == typeid(b)) return a.opEquals(b);
return a.opEquals(b) && b.opEquals(a);
}
---
)
$(LI Otherwise the expressions $(CODE a.opEquals(b)) and
$(CODE b.opEquals(a)) are tried. If both resolve to the same opEquals
function, then the expression is rewritten to be $(CODE a.opEquals(b)).
)
$(LI If one is a better match then the other, or one compiles and the other
does not, the one is selected.)
$(LI Otherwise, an error results.)
)

$(P If overridding Object.opEquals() for classes, the class member function
signature should look like:)
---
class C {
override bool opEquals(Object o) { ... }
}
---

$(P If structs declare an opEquals member function, it should follow the following
form:)
---
struct S {
int opEquals(ref const S s) { ... }
}
---

<h2><a name="compare">Overloading &lt; &lt;=, &gt; and &gt;=</a></h2>

$(P Comparison operations are rewritten as follows:)

$(TABLE2 Overloadable Unary Operators,
$(TR $(TH comparison) $(TH rewrite 1) $(TH rewrite 2) )

$(TR $(TD $(CODE a &lt; b)) $(TD $(CODE a.opCmp(b) &lt; 0)) $(TD $(CODE b.opCmp(a) &gt;= 0)))
$(TR $(TD $(CODE a &lt;= b)) $(TD $(CODE a.opCmp(b) &lt;= 0)) $(TD $(CODE b.opCmp(a) &gt; 0)))
$(TR $(TD $(CODE a &gt; b)) $(TD $(CODE a.opCmp(b) &gt; 0)) $(TD $(CODE b.opCmp(a) &lt;= 0)))
$(TR $(TD $(CODE a &gt;= b)) $(TD $(CODE a.opCmp(b) &gt;= 0)) $(TD $(CODE b.opCmp(a) &lt; 0)))
)

$(P Both rewrites are tried. If only one compiles, that one is taken.
If they both resolve to the same function, the first
rewrite is done. If they resolve to different functions, the best matching one
is used. If they both match the same, but are different functions, an ambiguity
error results.
)

$(P If overriding Object.opCmp() for classes, the class member function
signature should look like:)
---
class C {
override int opCmp(Object o) { ... }
}
---

$(P If structs declare an opCmp member function, it should follow the following
form:)
---
struct S {
int opCmp(ref const S s) { ... }
}
---

<h2><a name="FunctionCall">Function Call Operator Overloading $(I f)()</a></h2>

$(P The function call operator, (), can be overloaded by
declaring a function named $(B opCall):
)

-------
struct F
{
int $(B opCall)();
int $(B opCall)(int x, int y, int z);
}

void test()
{ F f;
int i;

i = f$(B ()); // same as i = f.opCall();
i = f$(B (3,4,5)); // same as i = f.opCall(3,4,5);
}
-------

$(P In this way a struct or class object can behave as if it
were a function.
)

<h2><a name="Assignment">Assignment Operator Overloading</a></h2>

$(P The assignment operator $(CODE =) can be overloaded if the
lvalue is a struct aggregate, and $(CODE opAssign)
is a member function of that aggregate.)

$(P The assignment operator cannot be overloaded for rvalues
that can be implicitly cast to the lvalue type.
Furthermore, the following parameter signatures for $(CODE opAssign)
are not allowed:)

---
opAssign(...)
opAssign(T)
opAssign(T, ...)
opAssign(T ...)
opAssign(T, U = defaultValue, etc.)
---

$(P where $(I T) is the same type as the aggregate type $(I A),
is implicitly
convertible to $(I A), or if $(I A) is a struct and $(I T)
is a pointer to a type that is
implicitly convertible to $(I A).
)

<h3>Index Assignment Operator Overloading</h2>

$(P If the left hand side of an assignment is an index operation
on a struct or class instance,
it can be overloaded by providing an opIndexAssign member function.
Expressions of the form $(CODE a[$(ARGUMENTS)] = c) are rewritten
as $(CODE a.opIndexAssign(c, $(ARGUMENTS))).
)

-------
struct A
{
int $(B opIndexAssign)(int value, size_t i1, size_t i2);
}

void test()
{ A a;

a$(B [)i,3$(B ]) = 7; // same as a.opIndexAssign(7,i,3);
}
-------

<h3>Slice Assignment Operator Overloading</h2>

$(P If the left hand side of an assignment is a slice operation
on a struct or class instance,
it can be overloaded by providing an opSliceAssign member function.
Expressions of the form $(CODE a[$(SLICE)] = c) are rewritten
as $(CODE a.opSliceAssign(c, $(SLICE2))), and
$(CODE a[] = c) as $(CODE a.opSliceAssign(c)).
)

-------
struct A
{
int $(B opSliceAssign)(int v); // overloads a[] = v
int $(B opSliceAssign)(int v, size_t x, size_t y); // overloads a[i .. j] = v
}

void test()
{ A a;
int v;

a$(B []) = v; // same as a.opSliceAssign(v);
a$(B [)3..4$(B ]) = v; // same as a.opSliceAssign(v,3,4);
}
-------

<h2><a name="OpAssign">Op Assignment Operator Overloading</a></h2>

$(P The following op assignment operators are overloadable:)

$(TABLE2 Overloadable Op Assignment Operators,

$(TR $(TD +=) $(TD -=) $(TD *=) $(TD /=) $(TD %=) $(TD ^^=) $(TD &amp;=) )
$(TR $(TD |=) $(TD ^=) $(TD &lt;&lt;=) $(TD &gt;&gt;=) $(TD &gt;&gt;&gt;=) $(TD ~=) $(TD &nbsp;) )

)

$(P The expression:)
---
a $(I op)= b
---

$(P is rewritten as:)

---
a.opOpAssign!("$(I op)")(b)
---

<h3>Index Op Assignment Operator Overloading</h3>

$(P If the left hand side of an $(I op)= is an index expression on
a struct or class instance and opIndexOpAssign is a member:)

---
a[$(ARGUMENTS)] $(I op)= c
---

$(P it is rewritten as:)

---
a.opIndexOpAssign!("$(I op)")(c, $(ARGUMENTS))
---

<h3>Slice Op Assignment Operator Overloading</h3>

$(P If the left hand side of an $(I op)= is a slice expression on
a struct or class instance and opSliceOpAssign is a member:)

---
a[$(SLICE)] $(I op)= c
---

$(P it is rewritten as:)

---
a.opSliceOpAssign!("$(I op)")(c, $(SLICE2))
---

$(P and)

---
a[] $(I op)= c
---

$(P it is rewritten as:)

---
a.opSliceOpAssign!("$(I op)")(c)
---

<h2><a name="Array">Index Operator Overloading</a></h2>

$(P The array index operator, $(CODE a[$(ARGUMENTS)]), can be overloaded by
declaring a function named $(B opIndex) with one
or more parameters.
)

-------
struct A
{
int $(B opIndex)(size_t i1, size_t i2, size_t i3);
}

void test()
{ A a;
int i;

i = a$(B [)5,6,7$(B ]); // same as i = a.opIndex(5,6,7);
}
-------

$(P In this way a struct or class object can behave as if it
were an array.
)

$(P If an index expression can be rewritten using opIndexAssign or opIndexOpAssign,
those are preferred over opIndex.
)


<h2><a name="Slice">Slice Operator Overloading</a></h2>

$(P Overloading the slicing operator means overloading expressions
like $(CODE a[]) and $(CODE a[$(SLICE)]).
This can be done by declaring a member function named $(B opSlice).
)

-------
class A
{
int $(B opSlice)(); // overloads a[]
int $(B opSlice)(size_t x, size_t y); // overloads a[i .. j]
}

void test()
{ A a = new A();
int i;
int v;

i = a$(B []); // same as i = a.opSlice();
i = a$(B [)3..4$(B ]); // same as i = a.opSlice(3,4);
}
-------

$(P If a slice expression can be rewritten using opSliceAssign or opSliceOpAssign,
those are preferred over opSlice.
)

<h2><a name="Dispatch">Forwarding</a></h2>

$(P Member names not found in a class or struct can be forwarded
to a template function named $(CODE opDispatch) for resolution.
)

---
import std.stdio;

struct S
{
void opDispatch(string s, T)(T i)
{
writefln("S.opDispatch('%s', %s)", s, i);
}
}

class C
{
void opDispatch(string s)(int i)
{
writefln("C.opDispatch('%s', %s)", s, i);
}
}

struct D
{
template opDispatch(string s)
{
enum int opDispatch = 8;
}
}

void main()
{
S s;
s.opDispatch!("hello")(7);
s.foo(7);

auto c = new C();
c.foo(8);

D d;
writefln("d.foo = %s", d.foo);
assert(d.foo == 8);
}
---

$(V1
$(P Overloading is accomplished by interpreting specially named
struct and class member functions as being implementations of unary and
binary operators. No additional syntax is used.
Expand All @@ -15,7 +632,6 @@ $(SPEC_S Operator Overloading,
$(LI $(LINK2 #Assignment, Assignment Operator Overloading))
$(V2
$(DOT $(LI $(LINK2 #Dot, Forwarding)))
$(LI $(LINK2 #Dispatch, Forwarding))
)
$(LI $(LINK2 #Future, Future Directions))
)
Expand All @@ -42,13 +658,6 @@ $(V2
$(TD $(CODE opCom))
)

$(V2
$(TR
$(TD *$(I e))
$(TD $(CODE opStar))
)
)

$(TR
$(TD $(I e)++)
$(TD $(CODE opPostInc))
Expand Down Expand Up @@ -160,9 +769,7 @@ void test()
$(TR $(TD /) $(TD no) $(TD $(CODE opDiv)) $(TD $(CODE opDiv_r)))

$(TR $(TD %) $(TD no) $(TD $(CODE opMod)) $(TD $(CODE opMod_r)))
$(V2
$(TR $(TD ^^) $(TD no) $(TD $(CODE opPow)) $(TD $(CODE opPow_r)))
)

$(TR $(TD &) $(TD yes) $(TD $(CODE opAnd)) $(TD $(CODE opAnd_r)))

$(TR $(TD |) $(TD yes) $(TD $(CODE opOr)) $(TD $(CODE opOr_r)))
Expand Down Expand Up @@ -200,9 +807,7 @@ $(V2
$(TR $(TD /=) $(TD no) $(TD $(CODE opDivAssign)) $(TD -))

$(TR $(TD %=) $(TD no) $(TD $(CODE opModAssign)) $(TD -))
$(V2
$(TR $(TD ^^=) $(TD no) $(TD $(CODE opPowAssign)) $(TD -))
)

$(TR $(TD &=) $(TD no) $(TD $(CODE opAndAssign)) $(TD -))

$(TR $(TD |=) $(TD no) $(TD $(CODE opOrAssign)) $(TD -))
Expand Down Expand Up @@ -623,63 +1228,19 @@ void main() {
)
)

$(V2
<h2><a name="Dispatch">Forwarding</a></h2>

$(P Member names not found in a class or struct can be forwarded
to a template function named $(CODE opDispatch) for resolution.
)

---
import std.stdio;

struct S
{
void opDispatch(string s, T)(T i)
{
writefln("S.opDispatch('%s', %s)", s, i);
}
}

class C
{
void opDispatch(string s)(int i)
{
writefln("C.opDispatch('%s', %s)", s, i);
}
}

struct D
{
template opDispatch(string s)
{
enum int opDispatch = 8;
}
}

void main()
{
S s;
s.opDispatch!("hello")(7);
s.foo(7);

auto c = new C();
c.foo(8);

D d;
writefln("d.foo = %s", d.foo);
assert(d.foo == 8);
}
---
)
<h2><a name="Future">Future Directions</a></h2>

$(P The operators $(CODE ! && || ?:) and a few others will
likely never be overloadable.
)
)
)

Macros:
TITLE=Operator Overloading
WIKI=OperatorOverloading

SUB=<sub>$0</sub>
ARGUMENTS=$(I b)$(SUB 1), $(I b)$(SUB 2), ... $(I b)$(SUB n)
SLICE=$(I i)..$(I j)
SLICE2=$(I i), $(I j)
FOO=