Operator overloading

From Vero - Wikipedia
Revision as of 01:53, 16 November 2025 by imported>Derek farn (Shorten long list to more well known languages)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Template:Short description Template:Use American English Template:Use dmy dates Template:Polymorphism In computer programming, operator overloading, sometimes termed operator ad hoc polymorphism, is a specific case of polymorphism, where different operators have different implementations depending on their arguments. Operator overloading is generally defined by a programming language, a programmer, or both.

Rationale Template:Anchor

Operator overloading is syntactic sugar, and is used because it allows programming using notation nearer to the target domain<ref>Template:Cite web</ref> and allows user-defined types a similar level of syntactic support as types built into a language. It is common, for example, in scientific computing, where it allows computing representations of mathematical objects to be manipulated with the same syntax as on paper.

Operator overloading does not change the expressive power of a language (with functions), as it can be emulated using function calls. For example, consider variables <syntaxhighlight lang="text" class="" style="" inline="1">a</syntaxhighlight>, <syntaxhighlight lang="text" class="" style="" inline="1">b</syntaxhighlight> and <syntaxhighlight lang="text" class="" style="" inline="1">c</syntaxhighlight> of some user-defined type, such as matrices:

<syntaxhighlight lang="text" class="" style="" inline="1">a + b * c</syntaxhighlight>

In a language that supports operator overloading, and with the usual assumption that the <syntaxhighlight lang="text" class="" style="" inline="1">*</syntaxhighlight> operator has higher precedence than the <syntaxhighlight lang="text" class="" style="" inline="1">+</syntaxhighlight> operator, this is a concise way of writing:

<syntaxhighlight lang="text" class="" style="" inline="1">Add(a, Multiply(b, c))</syntaxhighlight>

However, the former syntax reflects common mathematical usage.

Examples

In this case, the addition operator is overloaded to allow addition on a user-defined type <syntaxhighlight lang="text" class="" style="" inline="1">Time</syntaxhighlight> in C++:

<syntaxhighlight lang=Cpp> Time operator+(const Time& lhs, const Time& rhs) {

   Time temp = lhs;
   temp.seconds += rhs.seconds;
   temp.minutes += temp.seconds / 60;
   temp.seconds %= 60;
   temp.minutes += rhs.minutes;
   temp.hours += temp.minutes / 60;
   temp.minutes %= 60;
   temp.hours += rhs.hours;
   return temp;

} </syntaxhighlight>

Addition is a binary operation, which means it has two operands. In C++, the arguments being passed are the operands, and the <syntaxhighlight lang="text" class="" style="" inline="1">temp</syntaxhighlight> object is the returned value.

The operation could also be defined as a class method, replacing <syntaxhighlight lang="text" class="" style="" inline="1">lhs</syntaxhighlight> by the hidden <syntaxhighlight lang="text" class="" style="" inline="1">this</syntaxhighlight> argument; However, this forces the left operand to be of type <syntaxhighlight lang="text" class="" style="" inline="1">Time</syntaxhighlight>:

<syntaxhighlight lang=Cpp> // The "const" right before the opening curly brace means that `this` is not modified. Time Time::operator+(const Time& rhs) const {

   Time temp = *this;  // `this` should not be modified, so make a copy.
   temp.seconds += rhs.seconds;
   temp.minutes += temp.seconds / 60;
   temp.seconds %= 60;
   temp.minutes += rhs.minutes;
   temp.hours += temp.minutes / 60;
   temp.minutes %= 60;
   temp.hours += rhs.hours;
   return temp;

} </syntaxhighlight>

Note that a unary operator defined as a class method would receive no apparent argument (it only works from <syntaxhighlight lang="text" class="" style="" inline="1">this</syntaxhighlight>):

<syntaxhighlight lang=Cpp> bool Time::operator!() const {

   return hours == 0 && minutes == 0 && seconds == 0;

} </syntaxhighlight>

The less-than (<) operator is often overloaded to sort a structure or class:

<syntaxhighlight lang=Cpp> class IntegerPair { private:

   int x;
   int y;

public:

   explicit IntegerPair(int x = 0, int y = 0):
       x{x}, y{y} {}
   bool operator<(const IntegerPair& p) const {
       if (x == p.x) {
           return y < p.y;
       }
       return x < p.x;
   }

}; </syntaxhighlight>

Like with the previous examples, in the last example operator overloading is done within the class. In C++, after overloading the less-than operator (operator<), standard sorting functions can be used to sort some classes.

Starting in C++20 with the introduction of the three-way comparison operator (operator<=>), all the order operators can be defined by just defining that operator. The three-way comparison operator exists in many languages, including C++, Python, Rust, Swift, and PHP. Other languages, such as Java and C# instead use a method Comparable.compareTo(). <syntaxhighlight lang=Cpp> import std;

using std::strong_ordering;

class IntegerPair { private:

   int x;
   int y;

public:

   explicit IntegerPair(int x = 0, int y = 0):
       x{x}, y{y} {}
   // can be auto-generated with = default;
   strong_ordering operator<(const IntegerPair& p) const {
       if (strong_ordering cmp = x <=> p.x; cmp != strong_ordering::equal) {
           return cmp;
       }
       return y <=> p.y;
   }

}; </syntaxhighlight>

Criticisms

Operator overloading has often been criticized<ref>Template:Cite web</ref> because it allows programmers to reassign the semantics of operators depending on the types of their operands. For example, the use of the <syntaxhighlight lang="text" class="" style="" inline="1"><<</syntaxhighlight> operator in C++ <syntaxhighlight lang=Cpp inline>a << b</syntaxhighlight> shifts the bits in the variable <syntaxhighlight lang="text" class="" style="" inline="1">a</syntaxhighlight> left by <syntaxhighlight lang="text" class="" style="" inline="1">b</syntaxhighlight> bits if <syntaxhighlight lang="text" class="" style="" inline="1">a</syntaxhighlight> and <syntaxhighlight lang="text" class="" style="" inline="1">b</syntaxhighlight> are of an integer type, but if <syntaxhighlight lang="text" class="" style="" inline="1">a</syntaxhighlight> is an output stream then the above code will attempt to write a <syntaxhighlight lang="text" class="" style="" inline="1">b</syntaxhighlight> to the stream. Because operator overloading allows the original programmer to change the usual semantics of an operator and to catch any subsequent programmers by surprise, it is considered good practice to use operator overloading with care (the creators of Java decided not to use this feature,<ref>Template:Cite web</ref> although not necessarily for this reason).

Another, more subtle, issue with operators is that certain rules from mathematics can be wrongly expected or unintentionally assumed. For example, the commutativity of + (i.e. that <syntaxhighlight lang="text" class="" style="" inline="1">a + b == b + a</syntaxhighlight>) does not always apply; an example of this occurs when the operands are strings, since + is commonly overloaded to perform a concatenation of strings (i.e. <syntaxhighlight lang="text" class="" style="" inline="1">"bird" + "song"</syntaxhighlight> yields <syntaxhighlight lang="text" class="" style="" inline="1">"birdsong"</syntaxhighlight>, while <syntaxhighlight lang="text" class="" style="" inline="1">"song" + "bird"</syntaxhighlight> yields <syntaxhighlight lang="text" class="" style="" inline="1">"songbird"</syntaxhighlight>). A typical counterTemplate:Citation needed to this argument comes directly from mathematics: While + is commutative on integers (and more generally any complex number), it is not commutative for other "types" of variables. In practice, + is not even always associative, for example with floating-point values due to rounding errors. Another example: In mathematics, multiplication is commutative for real and complex numbers but not commutative in matrix multiplication.

Catalog

A classification of some common programming languages is made according to whether their operators are overloadable by the programmer and whether the operators are limited to a predefined set.

Operators Not overloadable Overloadable
New definable<ref>Completely new operators can be added.</ref>
Limited set

Timeline of operator overloading

1960s

The ALGOL 68 specification allowed operator overloading.<ref>Template:Cite web</ref>

Extract from the ALGOL 68 language specification (page 177) where the overloaded operators ¬, =, ≠, and abs are defined:

10.2.2. Operations on Boolean Operands
a) op ∨ = (bool a, b) bool:( a | true | b );
b) op ∧ = (bool a, b) bool: ( a | b | false );
c) op ¬ = (bool a) bool: ( a | false | true );
d) op = = (bool a, b) bool:( a∧b ) ∨ ( ¬b∧¬a );
e) op ≠ = (bool a, b) bool: ¬(a=b);
f) op abs = (bool a)int: ( a | 1 | 0 );

Note that no special declaration is needed to overload an operator, and the programmer is free to create new operators. For dyadic operators their priority compared to other operators can be set:

 prio max = 9;
 
 op max = (int a, b) int: ( a>b | a | b );
 op ++ = ( ref int a ) int: ( a +:= 1 );

1980s

Ada supports overloading of operators from its inception, with the publication of the Ada 83 language standard. However, the language designers chose to preclude the definition of new operators. Only extant operators in the language may be overloaded, by defining new functions with identifiers such as "+", "*", "&" etc. Subsequent revisions of the language (in 1995 and 2005) maintain the restriction to overloading of extant operators.

In C++, operator overloading is more refined than in ALGOL 68.<ref>Template:Cite web</ref>

1990s

Java language designers at Sun Microsystems chose to omit overloading.<ref>Template:Cite web</ref><ref>Template:Cite web</ref><ref>Template:Cite book</ref> When asked about operator overloading, Brian Goetz of Oracle responded "Value types first, then we can talk about it.", suggesting that it could potentially be added after Project Valhalla.<ref>Template:Cite tweet</ref>

Python allows operator overloading through the implementation of methods with special names.<ref>Template:Cite web</ref> For example, the addition (+) operator can be overloaded by implementing the method <syntaxhighlight lang="text" class="" style="" inline="1">obj.__add__(self, other)</syntaxhighlight>.

Ruby allows operator overloading as syntactic sugar for simple method calls.

Lua allows operator overloading as syntactic sugar for method calls with the added feature that if the first operand doesn't define that operator, the method for the second operand will be used.

2000s

Microsoft added operator overloading to C# in 2001 and to Visual Basic .NET in 2003. C# operator overloading is very similar in syntax to C++ operator overloading:<ref>Template:Cite web</ref> <syntaxhighlight lang="csharp"> public class Fraction {

   private int numerator;
   private int denominator;
   // ...
   public static Fraction operator +(Fraction lhs, Fraction rhs)
       => new Fraction(lhs.numerator * rhs.denominator + rhs.numerator * lhs.denominator, lhs.denominator * rhs.denominator);

} </syntaxhighlight>

Scala treats all operators as methods and thus allows operator overloading by proxy.

2010s

In Raku, the definition of all operators is delegated to lexical functions, and so, using function definitions, operators can be overloaded or new operators added. For example, the function defined in the Rakudo source for incrementing a Date object with "+" is:

<syntaxhighlight lang="raku"> multi infix:<+>(Date:D $d, Int:D $x) {

   Date.new-from-daycount($d.daycount + $x)

} </syntaxhighlight>

Since "multi" was used, the function gets added to the list of multidispatch candidates, and "+" is only overloaded for the case where the type constraints in the function signature are met. While the capacity for overloading includes +, *, >=, the postfix and term i, and so on, it also allows for overloading various brace operators: [x, y], x[y], x{y}, and x(y).

Kotlin has supported operator overloading since its creation by overwriting specially named functions (like plus(), inc(), rangeTo(), etc.)<ref>Template:Cite web</ref> <syntaxhighlight lang="Kotlin"> data class Point(val x: Int, val y: Int) {

   operator fun plus(other: Point): Point {
       return Point(this.x + other.x, this.y + other.y)
   }

} </syntaxhighlight>

Because both Kotlin and Java compile to [[Java class file|Template:Mono]], when converted back to Java this will just be represented as: <syntaxhighlight lang="Java"> public class Point {

   // fields and constructor...
   public Point plus(Point other) {
       return new Point(this.x + other.x, this.y + other.y);
   }

} </syntaxhighlight>

Operator overloading in Rust is accomplished by implementing the traits in std::ops.<ref>Template:Cite web</ref> <syntaxhighlight lang="Rust"> use std::ops::Add;

  1. [derive(Debug)]

struct Point {

   x: i32,
   y: i32

}

impl Point {

   pub fn new(x: i32, y: i32) -> Self {
       Point {x, y}
   }

}

impl Add for Point {

   type Output = Point;
   fn add(self, other: Point) -> Point {
       Point {
           x: self.x + other.y,
           y: self.y + other.y
       }
   }

}

fn main() {

   let p1: Point = Point::new(1, 2);
   let p2: Point = Point::new(3, 4);
   let sum: Point = p1 + p2;
   println!("Sum of p1 and p2: {:?}", sum);

} </syntaxhighlight>

See also

References

Template:Reflist

Template:Authority control