Movatterモバイル変換


[0]ホーム

URL:


D Logo
Menu
Search

Language Reference

table of contents

Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page.Requires a signed-in GitHub account. This works well for small changes.If you'd like to make larger changes you may want to consider usinga local clone.

Operator Overloading

Contents
  1. Unary Operator Overloading
    1. Postincremente++ and Postdecremente-- Operators
    2. Overloading Index Unary Operators
    3. Overloading Slice Unary Operators
  2. Cast Operator Overloading
    1. Boolean Operations
  3. Binary Operator Overloading
  4. Overloading the Comparison Operators
    1. Overloading== and!=
    2. Overloading<,<=,>, and>=
  5. Function Call Operator Overloading
    1. Static opCall
  6. Assignment Operator Overloading
    1. Index Assignment Operator Overloading
    2. Slice Assignment Operator Overloading
  7. Op Assignment Operator Overloading
    1. Index Op Assignment Operator Overloading
    2. Slice Op Assignment Operator Overloading
  8. Array Indexing and Slicing Operators Overloading
    1. Index Operator Overloading
    2. Slice Operator Overloading
    3. Dollar Operator Overloading
    4. Complete Example
  9. Forwarding
  10. D1 style operator overloading

Operator overloading is accomplished by rewriting operators whose operands are class or struct objects into calls to specially named members. No additional syntax is used.

Unary Operator Overloading

Overloadable Unary Operators
oprewrite
-ee.opUnary!("-")()
+ee.opUnary!("+")()
~ee.opUnary!("~")()
*ee.opUnary!("*")()
++ee.opUnary!("++")()
--ee.opUnary!("--")()

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;    }}void main(){    S s = {2};assert(-s == -2);}
Note:opUnary above can also be declared using a template parameter specialization:
int opUnary(string s :"-")()

Postincremente++ and Postdecremente-- Operators

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

Postfix Operator Rewrites
oprewrite
e--(auto t =e,e.opUnary!"--", t)
e++(auto t =e,e.opUnary!"++", t)

Overloading Index Unary Operators

Indexing can beoverloaded. A unary operation on an index expression can also be overloaded independently. This works for multidimensional indexing.

Overloadable Index Unary Operators
oprewrite
-a[b1,b2, ...bn]a.opIndexUnary!("-")(b1,b2, ...bn)
+a[b1,b2, ...bn]a.opIndexUnary!("+")(b1,b2, ...bn)
~a[b1,b2, ...bn]a.opIndexUnary!("~")(b1,b2, ...bn)
*a[b1,b2, ...bn]a.opIndexUnary!("*")(b1,b2, ...bn)
++a[b1,b2, ...bn]a.opIndexUnary!("++")(b1,b2, ...bn)
--a[b1,b2, ...bn]a.opIndexUnary!("--")(b1,b2, ...bn)
struct S{privateint[] a;void opIndexUnary(string s:"++")(size_t i) { ++a[i]; }}S s = {[4]};++s[0];assert(s.a[0] == 5);

Overloading Slice Unary Operators

Slicing can beoverloaded. A unary operation on a slice can also be overloaded independently.opIndexUnary is defined either with no function arguments for a full slice, or with two arguments for the start and end indices of the slice.

Overloadable Slice Unary Operators
oprewrite
-a[i..j]a.opIndexUnary!("-")(a.opSlice(i,j))
+a[i..j]a.opIndexUnary!("+")(a.opSlice(i,j))
~a[i..j]a.opIndexUnary!("~")(a.opSlice(i,j))
*a[i..j]a.opIndexUnary!("*")(a.opSlice(i,j))
++a[i..j]a.opIndexUnary!("++")(a.opSlice(i,j))
--a[i..j]a.opIndexUnary!("--")(a.opSlice(i,j))
-a[ ]a.opIndexUnary!("-")()
+a[ ]a.opIndexUnary!("+")()
~a[ ]a.opIndexUnary!("~")()
*a[ ]a.opIndexUnary!("*")()
++a[ ]a.opIndexUnary!("++")()
--a[ ]a.opIndexUnary!("--")()
struct S{privateint[] a;void opIndexUnary(string s:"--")() { --a[]; }}S s = {[1, 2]};--s[];assert(s.a == [0, 1]);
Note: For backward compatibility, if the above rewrites fail to compile andopSliceUnary is defined, then the rewritesa.opSliceUnary!(op)(i, j) anda.opSliceUnary!(op) are tried instead, respectively.

Cast Operator Overloading

To define how one type can be cast to another, define theopCast template method, which is used as follows:

Cast Operators
oprewrite
cast(type)ee.opCast!(type)()

Note thatopCast is only ever used with an explicitcast expression, except in the case of boolean operations (see next section).

struct S{void* mem;bool opCast(T)()if (is(T ==bool)) => mem !isnull;}S s = S(newint);auto b =cast(bool) s;assert(b);//b = s; // error

If the return type ofopCast differs from thetype parameter of thecast, then the result is implicitly converted totype.

Boolean Operations

Notably absent from the list of overloaded unary operators is the! logical negation operator. More obscurely absent is a unary operator to convert to abool result. Instead, for structs these are covered by a rewrite to:

opCast!(bool)(e)

So,

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

and similarly for other boolean conditional expressions andlogical operators used on the struct instance.

This only happens, however, for instances of structs. Class references are converted tobool by checking to see if the class reference is null or not.

Binary Operator Overloading

The following binary operators are overloadable:

Overloadable Binary Operators
+-*/%^^&
|^<<>>>>>~in

The expression:

aop b

is rewritten as one of:

a.opBinary!("op")(b)b.opBinaryRight!("op")(a)

and the one with the ‘better’ match is selected. It is an error for both to equally match. Example:

struct S{int[] data;// this ~ rhsint[] opBinary(string op :"~")(int rhs)    {return data ~ rhs;    }// lhs ~ thisint[] opBinaryRight(string op :"~")(int lhs)    {return lhs ~ data;    }}void main(){auto s = S([2,3]);assert(s ~ 4 == [2,3,4]);// opBinaryassert(1 ~ s == [1,2,3]);// opBinaryRight}

Operator overloading for a number of operators can be done at the same time. For example, if only the + or - operators are supported:

T opBinary(string op)(T rhs){staticif (op =="+")return data + rhs.data;elsestaticif (op =="-")return data - rhs.data;elsestaticassert(0,"Operator " ~ op ~" not implemented");}

To do them all en masse:

T opBinary(string op)(T rhs){returnmixin("data " ~ op ~" rhs.data");}

Note thatopIn andopIn_r have been deprecated in favor ofopBinary!"in" andopBinaryRight!"in" respectively.

Overloading the Comparison Operators

D allows overloading of the comparison operators==,!=,<,<=,>=,> via two functions,opEquals andopCmp.

The equality and inequality operators are treated separately from comparison operators because while practically all user-defined types can be compared for equality, only a subset of types have a meaningful ordering. For example, while it makes sense to determine if two RGB color vectors are equal, it is not meaningful to say that one color is greater than another, because colors do not have an ordering. Thus, one would defineopEquals for aColor type, but notopCmp.

Furthermore, even with orderable types, the order relation may not be linear. For example, one may define an ordering on sets via the subset relation, such thatx < y is true ifx is a (strict) subset ofy. Ifx andy are disjoint sets, then neitherx < y nory < x holds, but that does not imply thatx == y. Thus, it is insufficient to determine equality purely based onopCmp alone. For this reason,opCmp is only used for theRelExpression operators<,<=,>=, and>. The equality operators== and!= always employopEquals instead.

Therefore, it is the programmer's responsibility to ensure thatopCmp andopEquals are consistent with each other. IfopEquals is not specified for a struct, the compiler provides adefault version of it that does member-wise comparison. If this suffices, one may define onlyopCmp to customize the behaviour of theRelExpression operators. But if not, then a custom version ofopEquals should be defined as well, in order to preserve consistent semantics between the two kinds of comparison operators.

Finally, if a user-defined type is to be used as a key in the built-in associative arrays, then the programmer must ensure that the semantics ofopEquals andtoHash are consistent. If not, the associative array may not work in the expected manner.

Overloading== and!=

Expressions of the forma != b are rewritten as!(a == b).

Givena == b :

  1. If a and b are both class objects, then the expression is rewritten as:
    .object.opEquals(a, b)

    and that function is similar to:

    bool opEquals(Object a, Object b){if (ais b)returntrue;if (aisnull || bisnull)returnfalse;if (typeid(a) ==typeid(b))return a.opEquals(b);return a.opEquals(b) && b.opEquals(a);}
  2. Otherwise the expressionsa.opEquals(b) andb.opEquals(a) are tried. If both resolve to the sameopEquals function, then the expression is rewritten to bea.opEquals(b).
  3. If one is a better match than the other, or one compiles and the other does not, the first is selected.
  4. Otherwise, an error results.

If overridingObject.opEquals() for classes, the class member function should take anObject parameter and dynamically check that it is a compatible class, e.g.:

class C{int i;this(int i) {this.i = i; }overridebool opEquals(Object o)    {if (auto c =cast(C) o)return c.i == i;elseassert(0,__FUNCTION__ ~": Cannot compare a " ~typeid(o).toString);    }}staticassert(new C(2) ==new C(2));staticassert(new C(2) !=new C(3));

If structs declare anopEquals member function for the identity comparison, it could have several forms, such as:

struct S{// lhs should be mutable objectbool opEquals(const S s) { ... }// for r-values (e.g. temporaries)bool opEquals(refconst S s) { ... }// for l-values (e.g. variables)// both hand side can be const objectbool opEquals(const S s)const { ... }// for r-values (e.g. temporaries)}

Alternatively, declare a single templatedopEquals function with anauto ref parameter:

struct S{// for l-values and r-values,// with converting both hand side implicitly to constbool opEquals()(autorefconst S s)const { ... }}
Note: SeeStruct Equality for best practices on implementingopEquals for structs.

Overloading<,<=,>, and>=

Class references are first compared by reference. If they refer to different objects and neither isnull, they are then compared by calling a matchingopCmp method, as for structs.

Struct comparison operations are rewritten as follows:

Rewriting of comparison operations
comparisonrewrite 1rewrite 2
a<ba.opCmp(b)<0b.opCmp(a)>0
a<= ba.opCmp(b)<= 0b.opCmp(a)>= 0
a>ba.opCmp(b)>0b.opCmp(a)<0
a>= ba.opCmp(b)>= 0b.opCmp(a)<= 0

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.

struct B{int opCmp(int)         {return -1; }int opCmp(refconst S) {return -1; }int opCmp(refconst C) {return -1; }}struct S{int opCmp(refconst S) {return 1; }int opCmp(ref B)       {return 0; }}struct C{int opCmp(refconst B) {return 0; }}void main(){    S s;const S cs;    B b;    C c;assert(s > s);// s.opCmp(s) > 0assert(!(s < b));// s.opCmp(b) > 0  - S.opCmp(ref B) is exact matchassert(!(b < s));// s.opCmp(b) < 0  - S.opCmp(ref B) is exact matchassert(b < cs);// b.opCmp(s) < 0  - B.opCmp(ref const S) is  exact matchstaticassert(!__traits(compiles, b < c));// both C.opCmp and B.opcmp match exactly}

If overridingObject.opCmp() for classes, the class member function should take anObject parameter and dynamically check that it is a compatible class for the comparison (like when overridingopEquals).

class C{overrideint opCmp(Object o) { ... }}

If structs declare anopCmp member function, it should have the following form:

struct S{int opCmp(refconst S s)const { ... }}

Note thatopCmp is only used forRelExpression operators; expressions likea == b always useopEquals.

For structs, ifopCmp is defined butopEquals isn't, the compiler will supplya default version ofopEquals that performs member-wise comparison. However, the member-wise comparison may not be consistent with the user-definedopCmp.

It is up to the programmer to also supply a version ofopEquals when appropriate. Otherwise, aRelExpression likea <= b will behave inconsistently with equalities likea == b.

struct S{int i, j;int opCmp(refconst S s)const {return (i > s.i) - (i < s.i); }// ignore j}S a = {2, 3};S b = {2, 1};S c = {3, 0};assert(a < c);assert(a <= b);assert(!(a < b));// opCmp ignores jassert(a != b);// generated opEquals tests both i and j members
Best Practices: Using(i > s.i) - (i < s.i) instead ofi - s.i tocompare integers avoids overflow.

Function Call Operator Overloading

The function call operator,(), can be overloaded by declaring a function namedopCall:

struct F{int opCall();int opCall(int x,int y,int z);}void main(){    F f;int i;    i = f();// same as i = f.opCall();    i = f(3,4,5);// same as i = f.opCall(3,4,5);}

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

struct Multiplier{int factor;this(int num) { factor = num; }int opCall(int value) {return value * factor; }}void main(){    Multiplier m = Multiplier(10);// invoke constructorassert(m.factor == 10);int result = m(5);// invoke opCallassert(result == 50);}

Static opCall

static opCall also works as expected for a function call operator with type names.

struct Double{staticint opCall(int x) {return x * 2; }}void main(){int i = Double(2);assert(i == 4);}

Note that merely declaring astatic opCall automatically disablesstruct literal syntax.

Mixing struct constructors andstatic opCall is not allowed.

struct S{this(int i) {}static S opCall()// disallowed due to constructor    {return S.init;    }}

Note:static opCall can be used to simulate struct constructors with no arguments, but this is not recommended practice. Instead, the preferred solution is to use a factory function to create struct instances.

Assignment Operator Overloading

The assignment operator= can be overloaded if the left hand side is an aggregate, andopAssign is a member function of that aggregate.

For struct types, operator overloading for theidentity assignment is allowed, as well as assignment from other types.

struct S{// identity assignment, allowed.voidopAssign(S rhs);// not identity assignment, also allowed.voidopAssign(int);}S s;s = S();// Rewritten to s.opAssign(S());s = 1;// Rewritten to s.opAssign(1);

However for class types, assignment can only be overridden from a type which does not convert totypeof(this).

Rationale: All class types have reference semantics, so identity assignment rebinds the left-hand-side class reference to the one on the right. If this was overridable then it would conflict conceptually with a subclass implicitly converting to its base class.
class C{// If X is the same type as C or a type which is// implicitly convertible to C, then opAssign would// accept identity assignment, which is disallowed.// C opAssign(...);// C opAssign(X);// C opAssign(X, ...);// C opAssign(X ...);// C opAssign(X, U = defaultValue, etc.);// not an identity assignment - allowedvoidopAssign(int);}C c =new C();c =new C();// Rebinding referenceec = 1;// Rewritten to c.opAssign(1);

Index Assignment Operator Overloading

If the left hand side of an assignment is an index operation on a struct or class instance, it can be overloaded by providing anopIndexAssign member function. Expressions of the forma[b1,b2, ...bn] = c are rewritten asa.opIndexAssign(c,b1,b2, ...bn).

struct A{intopIndexAssign(int value, size_t i1, size_t i2);}void test(){    A a;    a[i,3] = 7;// same as a.opIndexAssign(7,i,3);}

Slice Assignment Operator Overloading

If the left hand side of an assignment is a slice operation on a struct or class instance, it can be overloaded by implementing anopIndexAssign member function that takes the return value of theopSlice function as parameter(s). Expressions of the forma[i..j] = c are rewritten asa.opIndexAssign(c,a.opSlice!0(i,j)), anda[] = c asa.opIndexAssign(c).

SeeArray Indexing and Slicing Operators Overloading for more details.

struct A{int opIndexAssign(int v);// overloads a[] = vint opIndexAssign(int v, size_t[2] slice);// overloads a[i .. j] = v    size_t[2] opSlice(size_t dim)(size_t i, size_t j);// overloads i .. j}void test(){    A a;int v;    a[] = v;// same as a.opIndexAssign(v);    a[3..4] = v;// same as a.opIndexAssign(v, a.opSlice!0(3,4));}

For backward compatibility, if rewritinga[i..j] asa.opIndexAssign(a.opSlice!0(i,j)) fails to compile, the legacy rewriteopSliceAssign(c,i,j) is used instead.

Op Assignment Operator Overloading

The following op assignment operators are overloadable:

Overloadable Op Assignment Operators
+=-=*=/=%=^^=&=
|=^=<<=>>=>>>=~= 

The expression:

aop= b

is rewritten as:

a.opOpAssign!("op")(b)
Example:
struct S{int i;void opOpAssign(string op:"+")(int rhs) { i += rhs; }}S s = {2};s += 3;assert(s.i == 5);

Index Op Assignment Operator Overloading

If the left hand side of anop= is an index expression on a struct or class instance andopIndexOpAssign is a member:

a[b1,b2, ...bn]op= c

it is rewritten as:

a.opIndexOpAssign!("op")(c,b1,b2, ...bn)

Slice Op Assignment Operator Overloading

If the left hand side of anop= is a slice expression on a struct or class instance andopIndexOpAssign is a member:

a[i..j]op= c

it is rewritten as:

a.opIndexOpAssign!("op")(c, a.opSlice(i,j))

and

a[]op= c

it is rewritten as:

a.opIndexOpAssign!("op")(c)

For backward compatibility, if the above rewrites fail andopSliceOpAssign is defined, then the rewritesa.opSliceOpAssign(c, i, j) anda.opSliceOpAssign(c) are tried, respectively.

Array Indexing and Slicing Operators Overloading

The array indexing and slicing operators are overloaded by implementing theopIndex,opSlice, andopDollar methods. These may be combined to implement multidimensional arrays.

Index Operator Overloading

Expressions of the formarr[b1,b2, ...bn] are translated intoarr.opIndex(b1,b2, ...bn). For example:

struct A{intopIndex(size_t i1, size_t i2, size_t i3);}void test(){    A a;int i;    i = a[5,6,7];// same as i = a.opIndex(5,6,7);}

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

If an index expression can be rewritten usingopIndexAssign oropIndexOpAssign, those are preferred overopIndex.

Slice Operator Overloading

Overloading the slicing operator means overloading expressions likea[] ora[i..j], where the expressions inside the square brackets contain slice expressions of the formi..j.

To overloada[], simply defineopIndex with no parameters:

struct S{int[] impl;int[] opIndex()    {return impl[];    }}void main(){auto s = S([1,2,3]);int[] t = s[];// calls s.opIndex()assert(t == [1,2,3]);}

To overload array slicing of the forma[i..j], two steps are needed. First, the expressions of the formi..j are translated viaopSlice!0 into objects that encapsulate the endpointsi andj. Then these objects are passed toopIndex to perform the actual slicing.

struct S{int[] impl;int[] opSlice(size_t dim: 0)(size_t i, size_t j)    {return impl[i..j];    }int[] opIndex()(int[] slice) {return slice; }}void main(){auto s = S([1, 2, 3]);int[] t = s[0..2];// calls s.opIndex(s.opSlice!0(0, 2))assert(t == [1, 2]);}

This design was chosen in order to support mixed indexing and slicing in multidimensional arrays; for example, in translating expressions likearr[1, 2..3, 4]. More precisely, an expression of the formarr[b1,b2, ...bn] is translated intoarr.opIndex(c1,c2, ...cn). Each argumentbi can be either a single expression, in which case it is passed directly as the corresponding argument ci toopIndex; or it can be a slice expression of the formxi..yi, in which case the corresponding argumentci toopIndex isarr.opSlice!i(xi,yi). Namely:

oprewrite
arr[1, 2, 3]arr.opIndex(1, 2, 3)
arr[1..2, 3..4, 5..6]arr.opIndex(arr.opSlice!0(1,2), arr.opSlice!1(3,4), arr.opSlice!2(5,6))
arr[1, 2..3, 4]arr.opIndex(1, arr.opSlice!1(2,3), 4)

Similar translations are done for assignment operators involving slicing, for example:

oprewrite
arr[1, 2..3, 4] = carr.opIndexAssign(c, 1, arr.opSlice!1(2, 3), 4)
arr[2, 3..4] += carr.opIndexOpAssign!"+"(c, 2, arr.opSlice!1(2, 3))

The intention is thatopSlice!i should return a user-defined object that represents an interval of indices along thei'th dimension of the array. This object is then passed toopIndex to perform the actual slicing operation. If only one-dimensional slicing is desired,opSlice may be declared without the compile-time parameteri.

Note that in all cases,arr is only evaluated once. Thus, an expression likegetArray()[1, 2..3, $-1]=c has the effect of:

auto __tmp = getArray();__tmp.opIndexAssign(c, 1, __tmp.opSlice!1(2,3), __tmp.opDollar!2 - 1);

where the initial function call togetArray is only executed once.

Note: For backward compatibility,a[] anda[i..j] can also be overloaded by implementingopSlice() with no arguments andopSlice(i,j) with two arguments, respectively. This only applies for one-dimensional slicing, and dates from when D did not have full support for multidimensional arrays. This usage ofopSlice is discouraged.

Dollar Operator Overloading

Within the arguments to array index and slicing operators,$ gets translated toopDollar!i, wherei is the position of the expression$ appears in. For example:

oprewrite
arr[$-1, $-2, 3]arr.opIndex(arr.opDollar!0 - 1, arr.opDollar!1 - 2, 3)
arr[1, 2, 3..$]arr.opIndex(1, 2, arr.opSlice!2(3, arr.opDollar!2))

The intention is thatopDollar!i should return the length of the array along itsi'th dimension, or a user-defined object representing the end of the array along that dimension, that is understood byopSlice andopIndex.

struct Rectangle{int width, height;int[][] impl;this(int w,int h)    {        width = w;        height = h;        impl =newint[][](w, h);    }int opIndex(size_t i1, size_t i2)    {return impl[i1][i2];    }int opDollar(size_t pos)()    {staticif (pos==0)return width;elsereturn height;    }}void main(){auto r = Rectangle(10,20);int i = r[$-1, 0];// same as: r.opIndex(r.opDollar!0, 0),// which is r.opIndex(r.width-1, 0)int j = r[0, $-1];// same as: r.opIndex(0, r.opDollar!1)// which is r.opIndex(0, r.height-1)}

As the above example shows, a different compile-time argument is passed toopDollar depending on which argument it appears in. A$ appearing in the first argument gets translated toopDollar!0, a$ appearing in the second argument gets translated toopDollar!1, and so on. Thus, the appropriate value for$ can be returned to implement multidimensional arrays.

Note thatopDollar!i is only evaluated once for eachi where$ occurs in the corresponding position in the indexing operation. Thus, an expression likearr[$-sqrt($), 0, $-1] has the effect of:

auto __tmp1 = arr.opDollar!0;auto __tmp2 = arr.opDollar!2;arr.opIndex(__tmp1 - sqrt(__tmp1), 0, __tmp2 - 1);

IfopIndex is declared with only one argument, the compile-time argument toopDollar may be omitted. In this case, it is illegal to use$ inside an array indexing expression with more than one argument.

Complete Example

The code example below shows a simple implementation of a 2-dimensional array with overloaded indexing and slicing operators. The explanations of the various constructs employed are given in the sections following.

struct Array2D(E){    E[] impl;int stride;int width, height;this(int width,int height, E[] initialData = [])    {        impl = initialData;this.stride =this.width = width;this.height = height;        impl.length = width * height;    }// Index a single element, e.g., arr[0, 1]ref E opIndex(int i,int j) {return impl[i + stride*j]; }// Array slicing, e.g., arr[1..2, 1..2], arr[2, 0..$], arr[0..$, 1].    Array2D opIndex(int[2] r1,int[2] r2)    {        Array2D result;auto startOffset = r1[0] + r2[0]*stride;auto endOffset = r1[1] + (r2[1] - 1)*stride;        result.impl =this.impl[startOffset .. endOffset];        result.stride =this.stride;        result.width = r1[1] - r1[0];        result.height = r2[1] - r2[0];return result;    }auto opIndex(int[2] r1,int j) {return opIndex(r1, [j, j+1]); }auto opIndex(int i,int[2] r2) {return opIndex([i, i+1], r2); }// Support for `x..y` notation in slicing operator for the given dimension.int[2] opSlice(size_t dim)(int start,int end)if (dim >= 0 && dim < 2)in {assert(start >= 0 && end <=this.opDollar!dim); }do    {return [start, end];    }// Support `$` in slicing notation, e.g., arr[1..$, 0..$-1].    @propertyint opDollar(size_t dim : 0)() {return width; }    @propertyint opDollar(size_t dim : 1)() {return height; }}void main(){auto arr = Array2D!int(4, 3, [        0, 1, 2,  3,        4, 5, 6,  7,        8, 9, 10, 11    ]);// Basic indexingassert(arr[0, 0] == 0);assert(arr[1, 0] == 1);assert(arr[0, 1] == 4);// Use of opDollarassert(arr[$-1, 0] == 3);assert(arr[0, $-1] == 8);// Note the value of $ differs by dimensionassert(arr[$-1, $-1] == 11);// Slicingauto slice1 = arr[1..$, 0..$];assert(slice1[0, 0] == 1 && slice1[1, 0] == 2  && slice1[2, 0] == 3 &&           slice1[0, 1] == 5 && slice1[1, 1] == 6  && slice1[2, 1] == 7 &&           slice1[0, 2] == 9 && slice1[1, 2] == 10 && slice1[2, 2] == 11);auto slice2 = slice1[0..2, 1..$];assert(slice2[0, 0] == 5 && slice2[1, 0] == 6 &&           slice2[0, 1] == 9 && slice2[1, 1] == 10);// Thin slicesauto slice3 = arr[2, 0..$];assert(slice3[0, 0] == 2 &&           slice3[0, 1] == 6 &&           slice3[0, 2] == 10);auto slice4 = arr[0..3, 2];assert(slice4[0, 0] == 8 && slice4[1, 0] == 9 && slice4[2, 0] == 10);}

Forwarding

Member names not found in a class or struct can be forwarded to a template function namedopDispatch 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)    {enumint 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);}

D1 style operator overloading

TheD1 operator overload mechanisms are deprecated.

Functions
Templates
Copyright © 1999-2026 by theD Language Foundation | Page generated byDdoc on Fri Feb 20 21:32:34 2026

[8]ページ先頭

©2009-2026 Movatter.jp