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, Slice Operator Overloading)) $(LI $(LINK2 #Dispatch, Forwarding)) )

Unary Operator Overloading

$(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; } ---

Postincrement $(I e)++ and Postdecrement $(I e)-- Operators

$(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))) ) )

Overloading Index Unary Operators

$(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)))) ) )

Overloading Slice Unary Operators

$(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!("--")())) ) )

Cast Operator Overloading

$(TABLE2 Cast Operators, $(TR $(TH$(I op)) $(TH$(I rewrite)) ) $(TR $(TD cast($(I type))$(I e)) $(TD $(CODE $(I e).opCast!($(I type))())) ) )

Boolean Operations

$(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. )

Binary Operator Overloading

$(P The following binary operators are overloadable:) $(TABLE2 Overloadable Binary Operators, $(TR $(TD +) $(TD -) $(TD *) $(TD /) $(TD %) $(TD ^^) $(TD &) ) $(TR $(TD |) $(TD ^) $(TD <<) $(TD >>) $(TD >>>) $(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 $(SINGLEQUOTE better) match is selected. It is an error for both to equally match. )

Overloading == and !=

$(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) { ... } } ---

Overloading < <=, > and >=

$(P Comparison operations are rewritten as follows:) $(TABLE2 Overloadable Unary Operators, $(TR $(TH comparison) $(TH rewrite 1) $(TH rewrite 2) ) $(TR $(TD $(CODE a < b)) $(TD $(CODE a.opCmp(b) < 0)) $(TD $(CODE b.opCmp(a) > 0))) $(TR $(TD $(CODE a <= b)) $(TD $(CODE a.opCmp(b) <= 0)) $(TD $(CODE b.opCmp(a) >= 0))) $(TR $(TD $(CODE a > b)) $(TD $(CODE a.opCmp(b) > 0)) $(TD $(CODE b.opCmp(a) < 0))) $(TR $(TD $(CODE a >= b)) $(TD $(CODE a.opCmp(b) >= 0)) $(TD $(CODE b.opCmp(a) <= 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) { ... } } ---

Function Call Operator Overloading $(I f)()

$(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. )

Assignment Operator Overloading

$(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). )

Index Assignment Operator Overloading

$(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); } -------

Slice Assignment Operator Overloading

$(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); } -------

Op Assignment Operator Overloading

$(P The following op assignment operators are overloadable:) $(TABLE2 Overloadable Op Assignment Operators, $(TR $(TD +=) $(TD -=) $(TD *=) $(TD /=) $(TD %=) $(TD ^^=) $(TD &=) ) $(TR $(TD |=) $(TD ^=) $(TD <<=) $(TD >>=) $(TD >>>=) $(TD ~=) $(TD  ) ) ) $(P The expression:) --- a $(I op)= b --- $(P is rewritten as:) --- a.opOpAssign!("$(I op)")(b) ---

Index Op Assignment Operator Overloading

$(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)) ---

Slice Op Assignment Operator Overloading

$(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) ---

Index Operator Overloading

$(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. )

Slice Operator Overloading

$(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. )

Forwarding

$(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. ) $(UL $(LI $(LINK2 #Unary, Unary Operator Overloading)) $(LI $(LINK2 #Binary, Binary Operator Overloading)) $(LI $(LINK2 #FunctionCall, Function Call Operator Overloading)) $(LI $(LINK2 #Array, Array Operator Overloading)) $(LI $(LINK2 #Assignment, Assignment Operator Overloading)) $(V2 $(DOT $(LI $(LINK2 #Dot, Forwarding))) ) $(LI $(LINK2 #Future, Future Directions)) )

Unary Operator Overloading

$(TABLE2 Overloadable Unary Operators, $(TR $(TH$(I op)) $(TH$(I opfunc)) ) $(TR $(TD -$(I e)) $(TD $(CODE opNeg)) ) $(TR $(TD +$(I e)) $(TD $(CODE opPos)) ) $(TR $(TD ~$(I e)) $(TD $(CODE opCom)) ) $(TR $(TD $(I e)++) $(TD $(CODE opPostInc)) ) $(TR $(TD $(I e)--) $(TD $(CODE opPostDec)) ) $(TR $(TD cast($(I type))$(I e)) $(TD $(CODE opCast)) ) ) $(P Given a unary overloadable operator $(I op) and its corresponding class or struct member function name $(I opfunc), the syntax: ) --- $(I op) a --- $(P where $(I a) is a class or struct object reference, is interpreted as if it was written as: ) --- a.$(I opfunc)() ---

Overloading ++$(I e) and --$(I e)

$(P Since ++$(I e) is defined to be semantically equivalent to ($(I e) += 1), the expression ++$(I e) is rewritten as ($(I e) += 1), and then checking for operator overloading is done. The situation is analogous for --$(I e). )

Examples

$(OL $(LI ------- class A { int $(B opNeg)(); } A a; -a; // equivalent to a.opNeg(); ------- ) $(LI ------- class A { int $(B opNeg)(int i); } A a; -a; // equivalent to a.opNeg(), which is an error ------- ) )

Overloading cast($(I type))$(I e)

$(P The member function $(I e).$(B opCast()) is called, and the return value of $(B opCast()) is implicitly converted to $(I type). Since functions cannot be overloaded based on return value, there can be only one $(B opCast) per struct or class. Overloading the cast operator does not affect implicit casts, it only applies to explicit casts. ) ------- struct A { int $(B opCast)() { return 28; } } void test() { A a; long i = cast(long)a; // i is set to 28L void* p = cast(void*)a; // error, cannot implicitly // convert int to void* int j = a; // error, cannot implicitly convert // A to int } -------

Binary Operator Overloading

$(TABLE2 Overloadable Binary Operators, $(TR $(TH $(I op)) $(TH commutative?) $(TH $(I opfunc)) $(TH $(I opfunc_r)) ) $(TR $(TD +) $(TD yes) $(TD $(CODE opAdd)) $(TD $(CODE opAdd_r))) $(TR $(TD -) $(TD no) $(TD $(CODE opSub)) $(TD $(CODE opSub_r))) $(TR $(TD *) $(TD yes) $(TD $(CODE opMul)) $(TD $(CODE opMul_r))) $(TR $(TD /) $(TD no) $(TD $(CODE opDiv)) $(TD $(CODE opDiv_r))) $(TR $(TD %) $(TD no) $(TD $(CODE opMod)) $(TD $(CODE opMod_r))) $(TR $(TD &) $(TD yes) $(TD $(CODE opAnd)) $(TD $(CODE opAnd_r))) $(TR $(TD |) $(TD yes) $(TD $(CODE opOr)) $(TD $(CODE opOr_r))) $(TR $(TD ^) $(TD yes) $(TD $(CODE opXor)) $(TD $(CODE opXor_r))) $(TR $(TD <<) $(TD no) $(TD $(CODE opShl)) $(TD $(CODE opShl_r))) $(TR $(TD >>) $(TD no) $(TD $(CODE opShr)) $(TD $(CODE opShr_r))) $(TR $(TD >>>) $(TD no) $(TD $(CODE opUShr)) $(TD $(CODE opUShr_r))) $(TR $(TD ~) $(TD no) $(TD $(CODE opCat)) $(TD $(CODE opCat_r))) $(TR $(TD ==) $(TD yes) $(TD $(CODE opEquals)) $(TD -)) $(TR $(TD !=) $(TD yes) $(TD $(CODE opEquals)) $(TD -)) $(TR $(TD <) $(TD yes) $(TD $(CODE opCmp)) $(TD -)) $(TR $(TD <=) $(TD yes) $(TD $(CODE opCmp)) $(TD -)) $(TR $(TD >) $(TD yes) $(TD $(CODE opCmp)) $(TD -)) $(TR $(TD >=) $(TD yes) $(TD $(CODE opCmp)) $(TD -)) $(TR $(TD =) $(TD no ) $(TD $(CODE opAssign)) $(TD -) ) $(TR $(TD +=) $(TD no) $(TD $(CODE opAddAssign)) $(TD -)) $(TR $(TD -=) $(TD no) $(TD $(CODE opSubAssign)) $(TD -)) $(TR $(TD *=) $(TD no) $(TD $(CODE opMulAssign)) $(TD -)) $(TR $(TD /=) $(TD no) $(TD $(CODE opDivAssign)) $(TD -)) $(TR $(TD %=) $(TD no) $(TD $(CODE opModAssign)) $(TD -)) $(TR $(TD &=) $(TD no) $(TD $(CODE opAndAssign)) $(TD -)) $(TR $(TD |=) $(TD no) $(TD $(CODE opOrAssign)) $(TD -)) $(TR $(TD ^=) $(TD no) $(TD $(CODE opXorAssign)) $(TD -)) $(TR $(TD <<=) $(TD no) $(TD $(CODE opShlAssign)) $(TD -)) $(TR $(TD >>=) $(TD no) $(TD $(CODE opShrAssign)) $(TD -)) $(TR $(TD >>>=) $(TD no) $(TD $(CODE opUShrAssign)) $(TD -)) $(TR $(TD ~=) $(TD no) $(TD $(CODE opCatAssign)) $(TD -)) $(TR $(TD in ) $(TD no ) $(TD $(CODE opIn) ) $(TD $(CODE opIn_r) )) ) $(P Given a binary overloadable operator $(I op) and its corresponding class or struct member function name $(I opfunc) and $(I opfunc_r), and the syntax: ) --- a $(I op) b --- the following sequence of rules is applied, in order, to determine which form is used: $(OL $(LI The expression is rewritten as both: --- a.$(I opfunc)(b) b.$(I opfunc_r)(a) --- If any $(I a.opfunc) or $(I b.opfunc_r) functions exist, then overloading is applied across all of them and the best match is used. If either exist, and there is no argument match, then it is an error. ) $(LI If the operator is commutative, then the following forms are tried: --- a.$(I opfunc_r)(b) b.$(I opfunc)(a) --- ) $(LI If $(I a) or $(I b) is a struct or class object reference, it is an error. ) )

Examples

$(OL $(LI ------- class A { int $(B opAdd)(int i); } A a; a + 1; // equivalent to a.opAdd(1) 1 + a; // equivalent to a.opAdd(1) ------- ) $(LI ------- class B { int $(B opDiv_r)(int i); } B b; 1 / b; // equivalent to b.opDiv_r(1) ------- ) $(LI ------- class A { int $(B opAdd)(int i); } class B { int $(B opAdd_r)(A a); } A a; B b; a + 1; // equivalent to a.opAdd(1) a + b; // equivalent to b.opAdd_r(a) b + a; // equivalent to b.opAdd_r(a) ------- ) $(LI ------- class A { int $(B opAdd)(B b); int $(B opAdd_r)(B b); } class B { } A a; B b; a + b; // equivalent to a.opAdd(b) b + a; // equivalent to a.opAdd_r(b) ------- ) $(LI ------- class A { int $(B opAdd)(B b); int $(B opAdd_r)(B b); } class B { int $(B opAdd_r)(A a); } A a; B b; a + b; // ambiguous: a.opAdd(b) or b.opAdd_r(a) b + a; // equivalent to a.opAdd_r(b) ------- ) )

Overloading == and !=

$(P Both operators use the $(CODE $(B opEquals)()) function. The expression $(CODE (a == b)) is rewritten as $(CODE a.$(B opEquals)(b)), and $(CODE (a != b)) is rewritten as $(CODE !a.$(B opEquals)(b)). ) $(P The member function $(CODE $(B opEquals)()) is defined as part of Object as: ) ------- $(V1 int)$(V2 bool) $(B opEquals)(Object o); ------- $(P so that every class object has a default $(CODE $(B opEquals)()). But every class definition which will be using == or != should expect to need to override opEquals. The parameter to the overriding function must be of type $(CODE Object), not the type for the class. ) $(P Structs and unions (hereafter just called structs) can provide a member function: ) ------- $(V1 int)$(V2 bool) $(B opEquals)(S s) ------- $(P or:) ------- $(V1 int)$(V2 bool) $(B opEquals)(S* s) ------- $(P where $(CODE S) is the struct name, to define how equality is determined.) $(P If a struct has no $(B opEquals) function declared for it, a bit compare of the contents of the two structs is done to determine equality or inequality. ) $(P $(B Note:) Comparing a reference to a class object against $(B null) should be done as: ) ------- if (a is null) ------- $(P and not as:) ------- if (a == null) ------- $(P The latter is converted to:) ------- if (a.$(B opEquals)(null)) ------- $(P which will fail if $(CODE $(B opEquals)()) is a virtual function.)

Overloading <, <=, > and >=

$(P These comparison operators all use the $(CODE $(B opCmp)()) function. The expression $(CODE (a $(I op) b)) is rewritten as $(CODE (a.$(B opCmp)(b) $(I op) 0)). The commutative operation is rewritten as $(CODE (0 $(I op) b.$(B opCmp)(a))) ) $(P The member function $(CODE $(B opCmp)()) is defined as part of Object as: ) ------- int $(B opCmp)(Object o); ------- $(P so that every class object has a $(CODE $(B opCmp)()). ) $(P $(CODE $(B opCmp)) for structs works analogously to $(CODE $(B opEquals)) for structs: ) ------- struct Pair { int a, b; int $(B opCmp)(Pair rhs) { if (a!=rhs.a) return a-rhs.a; return b-rhs.b; } } ------- $(P If a struct has no $(CODE $(B opCmp)()) function declared for it, attempting to compare two structs is an error. )

Rationale

$(P The reason for having both $(CODE $(B opEquals)) and $(CODE $(B opCmp)) is that:) $(UL $(LI Testing for equality can sometimes be a much more efficient operation than testing for less or greater than.) $(LI Having an $(CODE $(B opCmp)) defined in $(CODE Object) makes it possible to make associative arrays work generically for classes.) $(LI For some objects, testing for less or greater makes no sense. This is why $(CODE Object.$(B opCmp)) throws a runtime error. $(CODE $(B opCmp)) must be overridden in each class for which comparison makes sense.) ) $(P The parameter to $(B opEquals) and $(B opCmp) for class definitions must be of type $(CODE Object), rather than the type of the particular class, in order to override the $(CODE Object.$(B opEquals)) and $(CODE Object.$(B opCmp)) functions properly. )

Function Call Operator Overloading $(I f)()

$(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. )

Array Operator Overloading

Overloading Indexing $(I a)[$(I i)]

$(P The array index operator, [], can be overloaded by declaring a function named $(B opIndex) with one or more parameters. Assignment to an array can be overloaded with a function named $(B opIndexAssign) with two or more parameters. The first parameter is the rvalue of the assignment expression. ) ------- struct A { int $(B opIndex)(size_t i1, size_t i2, size_t i3); int $(B opIndexAssign)(int value, size_t i1, size_t i2); } void test() { A a; int i; i = a$(B [)5,6,7$(B ]); // same as i = a.opIndex(5,6,7); a$(B [)i,3$(B ]) = 7; // same as a.opIndexAssign(7,i,3); } ------- $(P In this way a struct or class object can behave as if it were an array. ) $(V1 $(P $(B Note:) Array index overloading currently does not work for the lvalue of an $(I op)=, ++, or -- operator. ) ) $(V2 $(P $(B Note:) To use array index overloading with the $(I op)=, ++, or -- operators, have the opIndex function return a reference type. This reference is then used as the lvalue for those operators. ) )

Overloading Slicing $(I a)[] and $(I a)[$(I i) .. $(I j)]

$(P Overloading the slicing operator means overloading expressions like $(CODE a[]) and $(CODE a[i .. j]). This can be done by declaring a function named $(B opSlice). Assignment to a slice can be done by declaring $(B opSliceAssign). ) ------- class A { int $(B opSlice)(); // overloads a[] int $(B opSlice)(size_t x, size_t y); // overloads a[i .. j] 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 = 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); a$(B []) = v; // same as a.opSliceAssign(v); a$(B [)3..4$(B ]) = v; // same as a.opSliceAssign(v,3,4); } -------

Assignment Operator Overloading

$(P The assignment operator $(CODE =) can be overloaded if the lvalue is a struct $(V1 or class) 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). ) $(V2 $(DOT

Forwarding

$(P Providing a struct or class member function $(CODE opDot) enables the forwarding of any names not found in the struct's scope to be forwarded to the return type of the $(CODE opDot) function. In other words: ) --- struct T { ... S opDot() { ... } } T t; ... t.m --- $(P is rewritten as:) --- t.opDot().m --- $(P if m does not exist as a member of the struct T.) $(P The members .sizeof, .init, .offsetof, .alignof, .mangleof and .stringof are not forwarded to $(CODE opDot).) --- struct S { int a, b, c; } struct T { S s; int b = 7; S* opDot() { return &s; // forwards to member s } } void main() { T t; t.a = 4; t.b = 5; t.c = 6; assert(t.a == 4); assert(t.b == 5); // T.b overrides S.b assert(t.c == 6); assert(t.s.b == 0); assert(t.sizeof == 4*4); // sizeof T, not sizeof S } --- ) )

Future Directions

$(P The operators $(CODE ! && || ?:) and a few others will likely never be overloadable. ) ) ) Macros: TITLE=Operator Overloading WIKI=OperatorOverloading SUB=$0 ARGUMENTS=$(I b)$(SUB 1), $(I b)$(SUB 2), ... $(I b)$(SUB n) SLICE=$(I i)..$(I j) SLICE2=$(I i), $(I j) FOO=