Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Operator overloading

A type that behaves like a number reads best when it is written like one: half + third rather than half.Plus( third ). An operator overload is a procedure the compiler calls when it sees an operator applied to your type.

function Gcd( byval a as int64, byval b as int64 ) as int64
    a = abs( a ) : b = abs( b )
    do while b <> 0
        dim as int64 t = a mod b
        a = b : b = t
    loop
    return a
end function

type Fraction
    declare constructor( byval n as int64 = 0, byval d as int64 = 1 )
    declare operator let( byval whole as int64 )  '' members: they change
    declare operator +=( byref o as Fraction )    '' or convert this
    declare operator cast( ) as ustring           '' object
    num as int64
    den as int64
end type

constructor Fraction( byval n as int64, byval d as int64 )
    dim as int64 g = Gcd( n, d )
    if d < 0 then g = -g             '' keep the sign on top
    num = n \ g : den = d \ g
end constructor

operator Fraction.let( byval whole as int64 )
    num = whole : den = 1
end operator

operator Fraction.cast( ) as ustring
    if den = 1 then return str( num )
    return num & "/" & den
end operator

'' Binary operators are global: neither side is privileged.
operator + ( byref a as Fraction, byref b as Fraction ) as Fraction
    return Fraction( a.num * b.den + b.num * a.den, a.den * b.den )
end operator

operator * ( byref a as Fraction, byref b as Fraction ) as Fraction
    return Fraction( a.num * b.num, a.den * b.den )
end operator

operator - ( byref a as Fraction ) as Fraction    '' unary minus
    return Fraction( -a.num, a.den )
end operator

operator = ( byref a as Fraction, byref b as Fraction ) as boolean
    return a.num = b.num andalso a.den = b.den
end operator

operator <> ( byref a as Fraction, byref b as Fraction ) as boolean
    return not ( a = b )
end operator

operator < ( byref a as Fraction, byref b as Fraction ) as boolean
    return a.num * b.den < b.num * a.den
end operator

operator Fraction.+=( byref o as Fraction )
    this = Fraction( num * o.den + o.num * den, den * o.den )
end operator

dim as Fraction half = Fraction( 1, 2 ), third = Fraction( 1, 3 )

print "1/2 + 1/3 = "; half + third
print "1/2 * 1/3 = "; half * third
print "-(1/2)    = "; -half
print "2/4 = 1/2:  "; Fraction( 2, 4 ) = half
print "1/3 < 1/2:  "; third < half; ", 1/3 <> 1/2: "; third <> half
print "1/2 + 1/3 * 1/2 = "; half + third * half; "  (* first)"

dim as Fraction total
total = 3                           '' operator let, from an int64
total += half
total += Fraction( 6, -4 )
print "3 + 1/2 - 3/2 = "; total

Output

1/2 + 1/3 = 5/6
1/2 * 1/3 = 1/6
-(1/2)    = -1/2
2/4 = 1/2:  true
1/3 < 1/2:  true, 1/3 <> 1/2: true
1/2 + 1/3 * 1/2 = 2/3  (* first)
3 + 1/2 - 3/2 = 2

An operator is defined like a function whose name is the operator: operator + ( byref a as Fraction, byref b as Fraction ) as Fraction. From then on, a + b on two fractions is a call to it.

Where an operator is declared depends on what it acts on:

OperatorsDeclaredWhy
+ - * /, =, <>, <, unary -, ...outside the typeneither operand is privileged
let, += and the other compound assignments, castinside the typethey change, or convert from, this object
[]inside the typesee Indexing

Declaring a binary operator inside the type is refused with TKO0790: operator '+' cannot be a member of a type.

A few things in the example are deliberate:

  • Each comparison is its own operator. Defining = does not define <>, so <> is written as not ( a = b ) to keep the two in agreement.
  • Precedence does not change. half + third * half multiplies first, because the expression is parsed before any operator procedure is chosen.
  • operator let( byval whole as int64 ) makes total = 3 work: it is assignment from another type. Assignment from another Fraction is the ordinary copy, covered in Copying.
  • operator cast( ) as ustring is what lets print show a fraction, as on the Display page.

A global operator sees only the type's public members, like any other code outside the type. That keeps operators from breaking the type's rules.

Status in this version

Inside a method, this cannot yet be an operand of an overloaded operator: this = this + o is refused with TKO0820: no operator '+' takes these operand types. Build the result from the fields, as += does here, or copy this into a local first.

Activities

  1. Add operator - for two fractions, and operator >.
  2. Add operator cast( ) as float64 and print cast( float64, third ).