Operator Overloading
Operator overloading enables you to allow a type to support an operator that is not supported by this type by default.
To overload an operator for a type, you must define a member function named after the operator. In this way, when the operator is called with an instance of this type, the operator function is automatically called.
The definition of an operator function is similar to that of a normal function. The differences are as follows:
- When defining an operator function, add the
operator
modifier before thefunc
keyword. - The number of parameters of an operator function must meet the requirements of the corresponding operator. For details, see Operators in the appendix.
- An operator function can be defined only in class, interface, struct, enum, and extend.
- An operator function has the semantics of an instance member function. Therefore, the
static
modifier cannot be used. - An operator function cannot be a generic function.
Note that the inherent priority and associativity of an operator is not changed after it is overloaded. For details, see Operators in the appendix.
Definition and Usage of Operator Functions
You can define operator functions in either of the following ways:
- For types that can directly contain function definitions (including
struct
,enum
,class
, andinterface
), operator functions can be added in the type definition. - Use
extend
to add operator functions to types so that operators can be overloaded for them. For types that cannot directly contain function definitions (types other thanstruct
,class
,enum
, andinterface
) or whose implementation cannot be changed, such asstruct
,class
,enum
, andinterface
defined by third parties, only this method can be used. For details, see Extension Overview.
The conventions on parameter types for operator functions are as follows:
-
For unary operators, operator functions have no parameter, and there is no requirement on the return value type.
-
For binary operators, operator functions have only one parameter, and there is no requirement on the return value type.
The following examples show how to define and use unary and binary operators.
-
is used to negate both member variablesx
andy
in aPoint
instance, returning a newPoint
object.+
is used to add up the values of the two member variablesx
andy
respectively in twoPoint
instances, returning a newPoint
object.open class Point { var x: Int64 = 0 var y: Int64 = 0 public init (a: Int64, b: Int64) { x = a y = b } public operator func -(): Point { Point(-x, -y) } public operator func +(right: Point): Point { Point(this.x + right.x, this.y + right.y) } }
Then, the
-
unary operator and the+
binary operator can be used directly forPoint
instances.main() { let p1 = Point(8, 24) let p2 = -p1 // p2 = Point(-8, -24) let p3 = p1 + p2 // p3 = Point(0, 0) }
-
Index operators (
[]
) have two forms:let a = arr[i]
(for obtaining a value) andarr[i] = a
(for assigning a value). They can be distinguished based on whether they contain the special named parameter value. Index operator overloading does not require that both forms be used at a time. You can use either one as required.In the expression
let a = arr[i, j, k...]
, the sequencei, j, k...
is used as the parameters of the overloaded operator[]
. Named parameters are not allowed. The parameter types of this form of the index operator, as well as the return type, are unrestricted.class A { operator func [](arg1: Int64, arg2: String): Int64 { return 0 } } func f() { let a = A() let b: Int64 = a[1, "2"] // b == 0 }
In
arr[i, j, k...] = a
, the sequencei, j, k...
is used as the parameters of the overloaded operator[]
, and the expressiona
on the right of=
is passed as a special named parameter. This named parameter can have any type, but its name must be value and it is not allowed to specify a default value. The return type of the assignment operator[]
must be Unit.Note that the use of the named value parameter is only as a marker and one does not need to pass it as a named parameter when an index operator is used to assign a value.
class A { operator func [](arg1: Int64, arg2: String, value!: Int64): Unit { return } } func f() { let a = A() a[1, "2"] = 0 }
Immutable types except
enum
cannot overload index assignmentarr[i] = a
. The compiler will report an error if the user provides an overload of index assignment in an immutable type other thanenum
. -
When the function call operator
()
is overloaded, the input parameters and return value can be of any type. The following is an example:open class A { public init() {} public operator func ()(): Unit {} } func test1() { let a = A() // Ok, A() calls the constructor of A. a() // Ok, a() is to call the operator () overloading function. }
The overloaded
()
operator cannot be called usingthis
orsuper
. The following is an example:open class A { public init() {} public init(x: Int64) { this() // Ok, this() calls the constructor of A. } public operator func ()(): Unit {} public func foo() { this() // Error, this() calls the constructor of A. super() // Error } } class B <: A { public init() { super() // Ok, super() calls the constructor of the super class. } public func goo() { super() // Error } }
For
enum
types, the constructor form has priority when a constructor and an overloaded()
operator function are candidates. The following is an example:enum E { Y | X | X(Int64) public operator func ()(p: Int64) {} public operator func ()(p: Float64) {} } main() { let e = X(1) // Ok, X(1) is to call the constructor X(Int64). X(1.0) // Ok, X(1.0) is to call the operator () overloading function. let e1 = X e1(1) // Ok, e1(1) is to call the operator () overloading function. Y(1) // Ok, Y(1) is to call the operator () overloading function. }
Operators That Can Be Overloaded
The following table lists all operators that can be overloaded (in descending order of priority):
Operator | Description |
---|---|
() | Function call |
[] | Indexing |
! | NOT |
- | Negative |
** | Power |
* | Multiply |
/ | Divide |
% | Remainder |
+ | Add |
- | Subtract |
<< | Bitwise left shift |
>> | Bitwise right shift |
< | Less than |
<= | Less than or equal |
> | Greater than |
>= | Greater than or equal |
== | Equal |
!= | Not equal |
& | Bitwise AND |
^ | Bitwise XOR |
| | Bitwise OR |
Note:
- If a binary operator other than a relational operator (
<
,<=
,>
,>=
,==
, or!=
) is overloaded for a type, and the return type of the operator function is the same as the type of a left operand or its subtype, the type supports the corresponding compound assignment operator. If the return type of an operator function is different from the type of a left operand or its subtype, a type mismatch error is reported when the corresponding compound assignment operator is used. - Cangjie does not support custom operators. That is, only operators in the preceding table can be overloaded.
- If a type
T
already supports an overloaded operator, a redefinition error is reported when a new operator function with the same signature is implemented forT
through an extension. For example, a redefinition error is reported when you overload an arithmetic operator, a bitwise operator, a relational operator, or other operators that are already supported by the number type with the same signature for the number type, when you overload a relational operator with the same signature for theRune
type, or when you overload a logical operator, the equal-to operator, or the not-equal-to operator with the same signature for theBool
type.