Monthly Archives: June 2016

Operator overloading for structs

D’s structs can use operator overloading. This is accomplished by defining specially named member functions.

Operator overloading is the ability to define language operators (e.g +, +=) on custom types.

In this tutorial, we will focus on designing a rational number struct (called Rational).
We will also overload operators of it to make its usage feel more natural.

You can read the official D documentation on operator overloading here.

Here is our first version.

We can create a Rational like this: auto r = Rational(48, 18); Calling writeln(r.n, "/", r.d); correctly displays 8/3.

Okay let’s move on to what this tutorial is about – operator overloading.

1. Binary Operators

First, we’d like to be able to add (+), subtract (-), multiply (*) and divide (/) rationals.
These are binary operators, which means they take two arguments.

To overload binary operators, we must define opBinary!op. This is a template function where op is the operator, as a string.

  • Unlike C++, we don’t need to define multiple overloads for each binary operator. In D, one is enough.
  • By using auto ref, the compiler will pass by value or by reference depending on the situation.

Now we can do this:

2. Unary Operators

We would also like to overload unary (to invert a rational) and + (not necessary, just for completeness).

Overloading of unary operators is done with opUnary!op. It is similar to opBinary!op, but instead it takes no arguments.

So here’s our implementation for unary + and –

3. Op Assignment Operators (+=, etc)

+=, -=, *= and /= would also be useful. Those can be overloaded with opOpAssign!op.

Note that we return ref Rational, a reference to this object, as these operators modify the object rather than returning a new one.

4. Equality Operators (==, !=)

To compare rationals for equality, we need to the define opEquals. Note that there’s no opNotEquals (or anything along those lines) – Expressions like a != b are automatically re-written by the compiler to the form !(a == b).

Here’s the implementation. We simply compare the fields that matter to each specific case. In our case, we must compare both numerator and denominator.

The extra set of empty parentheses is to make the the function a template (as auto ref only works with templates).

5. Comparison Operators (<, >, ...)

Finally, we would like to able to compare rationals using <, > – the comparison operators.

This can be accomplished with opCmp.

You might wonder why the notion of equality is not defined in terms of comparison operators.
From the official D documentation:


The equality and inequality operators are treated separately 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 define opEquals for a Color type, but not opCmp.

If opCmp is defined but opEquals isn’t, the compiler will supply a default version of opEquals that performs member-wise comparison.

Result

Here’s what the result looks like:

Note: It would be really useful if we had defined overloads that accept int – so that we could to things like Rational(1, 1) + 5 . However, this can be done with the same logic with what we ‘ve done above. Just provide an implementation of your operator of choice that takes an int as its parameter.