Function Overloading
Definition
In Cangjie, function overloading refers to the situation where a function name corresponds to multiple function definitions in a scope.
-
Overloaded functions have the same name but differ in the number or type of their parameters. The following is an example:
// Scenario 1 func f(a: Int64): Unit { } func f(a: Float64): Unit { } func f(a: Int64, b: Float64): Unit { }
-
When two generic functions have the same name, all the generic parameters of one of themn are renamed to be the same as the other. If the parameter types of the resulting functions are different (including the renamed generic types as well as any non-generic types), then they are overloaded functions. Otherwise, a duplicate definition error is reported (constraints of type variables are not taken into account). The following is an example:
interface I1{} interface I2{} func f1<X, Y>(a: X, b: Y) {} func f1<Y, X>(a: X, b: Y) {} // Ok: after renaming generic type parameters, it will be 'func f1<X, Y>(a: Y, b: X)' func f2<T>(a: T) where T <: I1 {} func f2<T>(a: T) where T <: I2 {} // Error, not overloading
-
If two constructors in the same class have different parameters, they are overloaded constructors. The following is an example:
// Scenario 2 class C { var a: Int64 var b: Float64 public init(a: Int64, b: Float64) { this.a = a this.b = b } public init(a: Int64) { b = 0.0 this.a = a } }
-
If a primary constructor and the
init
constructor in the same class have different parameters, they are overloaded constructors (the primary constructor and theinit
constructor are considered to have the same name). The following is an example:// Scenario 3 class C { C(var a!: Int64, var b!: Float64) { this.a = a this.b = b } public init(a: Int64) { b = 0.0 this.a = a } }
-
Two functions defined in different scopes can be overloaded in a scope that is visible to both of them. The following is an example:
// Scenario 4 func f(a: Int64): Unit { } func g() { func f(a: Float64): Unit { } }
-
Two functions defined in a parent class and a child class respectively can be overloaded in a scope that is visible to both of them. The following is an example:
// Scenario 5 open class Base { public func f(a: Int64): Unit { } } class Sub <: Base { public func f(a: Float64): Unit { } }
Only function declarations are allowed to introduce overloading, with the following exceptions:
- In a class, interface, enum or struct type, a member function cannot have both static and non-static overloads.
- In an enum type, a constructor and a member function (static or non-static) cannot be overloaded.
Variable declarations cannot introduce function overloading, even if they are of function type, so declaring multiple variables with the same name in the same scope is not allowed. In the following example, two variables with the same name are functions with different parameter types, but they are not introduced by function declarations. Therefore, they cannot be overloaded and a compilation error (redefinition error) is reported.
main() {
var f: (Int64) -> Unit
var f: (Float64) -> Unit
}
In the following example, although the variable f
is of function type, a compilation error (redefinition error) is reported because a variable and a function cannot have the same name:
main() {
var f: (Int64) -> Unit
func f(a: Float64): Unit { // Error, functions and variables cannot have the same name.
}
}
In the following example, although the f
static member function and the f
instance member function have different parameter types, a compilation error is reported because a static member function and an instance member function in the same class cannot be overloaded:
class C {
public static func f(a: Int64): Unit {
}
public func f(a: Float64): Unit {
}
}
Function Overloading Resolution
When encountering a function call, all functions that can be called (functions that are visible in the current scope and can pass the type check) form a candidate set. As there may be multiple functions in the candidate set, function overloading resolution is required to select a function from it. The rules are as follows:
-
A function in a higher-level scope is preferred. In functions with nested expressions or functions, the inner scopes are of higher level.
In the following example, when
g(Sub())
is called in the function body ofinner
, the candidate set includesg
defined ininner
andg
defined outsideinner
. The definition ofg
ininner
is selected during function resolution because it is in a higher-level scope.open class Base {} class Sub <: Base {} func outer() { func g(a: Sub) { print("1") } func inner() { func g(a: Base) { print("2") } g(Sub()) // Output: 2 } }
-
If there are still multiple functions in a higher-level scope, the most specific function is selected. Assume that there are functions f and g and given arguments. If g can be called when f is called but the reverse is not true, f is a more specific function than g. If there is no unique function that is most specific, an error is reported.
In the following example, the two functions both named
g
are defined in the same scope, and the more specific functiong(a: Sub): Unit
is selected:open class Base {} class Sub <: Base {} func outer() { func g(a: Sub) { print("1") } func g(a: Base) { print("2") } g(Sub()) // Output: 1 }
-
Child classes and parent classes are considered to be in the same scope. In the following example, a function
g
is defined in the parent class, and another functiong
is defined in the child class. Whens.g(Sub())
is called, the two functionsg
are considered to be in the same scope during resolution, and the better matched functiong(a: Sub): Unit
defined in the parent class is selected.open class Base { public func g(a: Sub) { print("1") } } class Sub <: Base { public func g(a: Base) { print("2") } } func outer() { let s: Sub = Sub() s.g(Sub()) // Output: 1 }