Indexing Operators

Learn to overload indexing operators for structs.

Use of indexing operators

opIndex, opIndexAssign, opIndexUnary, opIndexOpAssign, and opDollar make it possible to use indexing operators on user-defined types similar to arrays as in object[index].

Unlike arrays, these operators support multi-dimensional indexing as well. Multiple index values are specified as a comma-separated list inside the square brackets (e.g., object[index0, index1]). In the following examples, we will use these operators only with a single dimension and cover their multidimensional uses in the more templates chapter.

Here, e is a variable of type int. opIndex is for element access. The index that is specified inside the brackets becomes the parameter of the operator function:

e = deque[3]; // the element at index 3
e = deque.opIndex(3); // the equivalent of the above

opIndexAssign is for assigning a value to an element. The first parameter is the value that is being assigned, and the second parameter is the index of the element:

deque[5] = 55; // assign 55 to the element at index 5 
deque.opIndexAssign(55, 5); // the equivalent of the above

opIndexUnary is similar to opUnary. The difference is that the operation is applied to the element at the specified index:

++deque[4]; // increment the element at index 4 
deque.opIndexUnary!"++"(4); // the equivalent of the above

opIndexOpAssign is similar to opOpAssign. The difference is that the operation is applied to an element:

deque[6] += 66; // add 66 to the element at index 6 
deque.opIndexOpAssign!"+"(66, 6);// the equivalent of the above

opDollar defines the $ character that is used during indexing and slicing. It is for returning the number of elements in the container:

e = deque[$ - 1]; // the last element
e = deque[deque.opDollar() - 1]; // the equivalent of the above

Indexing operators example

Double-ended queue is a data structure that is similar to arrays, but it also provides efficient insertion at the head of the collection.

In contrast, inserting at the head of an array is a relatively slow operation as it requires moving the existing elements to a newly created array.

One way of implementing a double-ended queue is to use two arrays in the background but to use the first one in reverse. The element that is conceptually inserted at the head of the queue is actually appended to the head array. As a result, this operation is as efficient as appending the element to the end.

The following struct implements a double-ended queue that overloads the operators that we have seen in this section. The deque variable in the following examples is an object of struct DoubleEndedQueue:

Get hands-on with 1200+ tech skills courses.