Overloading

Function/method overloading involves declaring multiple functions or methods with the same name but having different implementations.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
int add(int a, int b) {
    return add(a, b, 0);
}
int add(int a, int b, int c) {
    return add(a, b, c, 0);
}
int add(int a, int b, int c, int d) {
    return a + b + c + d;
}
 
add(1, 2);    // 3
add(1, 1, 5); // 7
add(5, 10, 5, 10); // 30

In the above example, there are multiple functions declared with the name add. However, each function is implemented differently. Each "overload" takes different parameters, and each overload further executes different logic.

In this simple example, we have an overloaded add function, with each overload taking two, three, and four int parameters, respectively. However, you will notice the first overload is calling the second overload, and the second overload is calling the last overload. This is a common function overloading pattern: rather than repeating similar logic that was handled by a more generalized overload, we keep generalizing further until we reach the final generalization.

Two overloads cannot have the same parameter types and return type; otherwise, a compiler error will be raised. However, two overloads with different parameter types and different return types are possible.

Free Functions

Free functions are non-member functions. (Member functions are known as "methods.")

When overloading free functions, all overloads must be present in the same scope.

Method Overloading

Methods are member functions. Thus, methods would include all functions declared as a member of a class.

Methods can be overloaded as long as they are part of the same class or if they were inherited from a base class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import System;
 
class Foo
{
    public int baz() {
        return 1;
    }
     
    // Overload 'baz' method of same class
    public int baz(int qux, int quux) {
        return qux + quux;
    }
}
  
class Bar : Foo
{
    // Overload inherited 'baz' method
    public int baz(int qux) {
        return qux;
    }
}
 
Bar bar = new Bar();
Console.log(bar.baz());     // 1
Console.log(bar.baz(100));  // 100
Console.log(bar.baz(1, 1)); // 2

Rest Parameters

With rest parameters, if a parameter is defined at the same index (or beyond the index) as a rest parameter, and the parameter has the same type as the rest parameter, an ambiguity can occur.

For instance:

1
2
void myFunction(int foo, ...int bar) { /* ... */ }
void myFunction(int foo, int bar, int baz) { /* ... */ }

The above code will result in a compiler error. There are two overloads for myFunction. At the second parameter of the first overload, there is a rest parameter with type int. This means another overload cannot have int for the first parameter and int in the second parameter and beyond (due to the potential for ambiguity with the rest parameter).

However, if at least one parameter beyond the second parameter is not an int type, the potential ambiguity would be remedied:

1
2
void myFunction(int foo, ...int bar) { /* ... */ }
void myFunction(int foo, int bar, int baz, string qux) { /* ... */ }

Passing by Reference

The JS++ compiler prohibits overloaded functions from being passed by reference.

If you wish to pass an overloaded function by reference, declare a proxy function:

1
2
3
4
5
6
7
8
int sum(int x) { /* ... */ }
int sum(int x, int y) { /* ... */ }
 
int proxy(int x, int y) {
    return sum(x, y);
}
 
someFunctionCall(proxy); // we can now pass by reference via the proxy

The proxy function should call a specific overload of the overloaded function.

Casting and Conversions

When a specific overload has not been explicitly declared, casting and conversions can be used.

Types may have implicit conversions to another type. For instance, short can be implicitly converted to int. Therefore, if a function is defined with an int parameter, but data with type short was passed, the function can still be called due to the implicit conversion:

1
2
3
4
5
void foo(int s) {}
void foo(unsigned int s) {}
 
short x = 1;
foo(x);

However, specific overloads always take precedence over overloads available through conversion. When conversions are used, they happen in the following order:

  1. byte
  2. signed byte
  3. short
  4. unsigned short
  5. int
  6. unsigned int
  7. long
  8. unsigned long
  9. char
  10. float
  11. double
  12. external

We can also "force" a specific overload to be chosen via type casting:

1
2
3
4
void foo(short s) {}
void foo(unsigned short s) {}
 
foo((short) 1);

See Also

Share

HTML | BBCode | Direct Link