Function Overloading - HimxhMC/HimDSL GitHub Wiki

函数重载 / Function Overloading

什么是重载 / What Overloading Is

在同一个脚本里,可以有多个同名但参数不同的函数。这叫重载。例如:

A script may contain several functions with the same name but different parameters. This is overloading:

int max(int a, int b) {
    if (a > b) return a;
    return b;
}

double max(double a, double b) {
    if (a > b) return a;
    return b;
}

两个 max 名字相同,一个接收两个 int,一个接收两个 double。调用时,HimDSL 会根据参数类型和个数选择合适的那一个。

Both are named max; one takes two ints, the other two doubles. When you call it, HimDSL selects the version matching the argument types and count.


重载的判断依据 / What Distinguishes Overloads

函数能否重载,取决于参数列表,与返回类型无关。

Whether two functions overload depends on the parameter list, not the return type.

可以重载:

May overload:

int    f(int x)                { return x; }
int    f(int x, int y)         { return x + y; }          // 参数个数不同
string f(string s)             { return s; }              // 参数类型不同

不能重载(参数列表完全相同):

Cannot overload (identical parameter lists):

int f(int x) { return x; }
double f(int x) { return x * 1.0; }    // ✗ 报错:与上一个签名重复

即使返回类型不同,只要参数列表一致,就算重复定义,会报错。返回类型不参与重载判断。

Even with different return types, identical parameter lists count as duplicates and error. Return types do not distinguish overloads.


重载的解析顺序 / Resolution Order

调用重载函数时,HimDSL 按下面的顺序选择:

When calling an overloaded function, HimDSL chooses in this order:

  1. 参数个数。

  2. 参数类型。

  3. 有具体类型声明的优先。

  4. Argument count.

  5. Argument types.

  6. Functions with declared types beat untyped ones.

最常见的用法是通过参数类型区分:

The most common form distinguishes by argument types:

string describe(int n)    { return "整数 " + n; }
string describe(string s) { return "字符串 " + s; }
string describe(double d) { return "小数 " + d; }

int main() {
    runcmd("CONSOLE", "say " + describe(42));         // 整数 42
    runcmd("CONSOLE", "say " + describe("hello"));    // 字符串 hello
    runcmd("CONSOLE", "say " + describe(3.14));       // 小数 3.14
}

每次调用都根据实参类型选到相应的版本。

Each call selects the matching version by argument type.


参数个数优先 / Count Comes First

如果参数个数不同,HimDSL 会先按个数缩小范围,再按类型选择:

When counts differ, HimDSL narrows by count first, then by type:

int add(int a, int b)         { return a + b; }
int add(int a, int b, int c)  { return a + b + c; }

int main() {
    add(1, 2);        // 调用第一个
    add(1, 2, 3);     // 调用第二个
}

参数个数完全不匹配的调用不会自动补齐或截断,而是报错。

A call whose count matches no overload does not auto-pad or truncate; it errors.


类型匹配的宽松性 / Loose Type Matching

类型匹配不是严格相等,而是能否转换。例如整数可以传进期望 double 的参数:

Type matching is not strict equality but convertibility. For example, an integer may pass where a double is expected:

double half(double x) { return x / 2.0; }

int main() {
    double a = half(5);        // 5 被视为 5.0
    double b = half(5.0);
}

反过来,double 传给 int 参数不会自动转换,需要在调用处手动 toInt:

Conversely, a double passed to an int parameter is not auto-converted; call toInt explicitly:

int negate(int x) { return -x; }

int main() {
    int a = negate(5);              // ✓
    int b = negate(toInt(5.5));     // ✓ 显式转换
    int c = negate(5.5);            // ✗ 类型不匹配
}

这种"只宽不窄"的规则能帮你避免意外丢失精度。

This "widen only" rule prevents accidental precision loss.


重载与歧义 / Overloading and Ambiguity

当两个重载同样匹配一个调用时,HimDSL 会报"重载歧义"错误,而不是随便挑一个。例如:

When two overloads match a call equally well, HimDSL raises an "ambiguous overload" error instead of picking one arbitrarily:

int    f(int x, int y)       { return x + y; }
double f(double x, int y)    { return x + y; }

int main() {
    f(1, 2);         // 两个都能接受,报歧义
}

修复方式是显式转换实参,让其中一个候选明确胜出:

Fix by explicitly converting the arguments so one candidate wins:

f(1.0, 2);           // 明确选第二个

或者干脆不要写这种易混的重载。

Or simply avoid such easily-confused overloads.

经验法则:重载只用来区分明显不同的情况。例如参数类型不同、参数个数不同。两个重载如果只差一个类型,而调用者很难一眼看出选哪个,就应该换个名字。

Rule of thumb: overload only to distinguish clearly different cases. If two overloads differ by a single type and the caller cannot tell which will be chosen, rename one.


重载与默认参数 / Overloading and Defaults

HimDSL 没有默认参数。想要"可选参数"的效果,只能写多个重载,最短的那个调用最长的那个:

HimDSL has no default parameters. To fake optional arguments, write several overloads where the shortest delegates to the longest:

string tag(string text) {
    return tag(text, "info");
}

string tag(string text, string level) {
    return "[" + level + "] " + text;
}

int main() {
    runcmd("CONSOLE", "say " + tag("启动完成"));         // [info] 启动完成
    runcmd("CONSOLE", "say " + tag("有异常", "warn"));   // [warn] 有异常
}

这就是 Java 里常见的写法。每个重载做一件小事,最短的调用最长的。

This is common in Java. Each overload does one small thing; the shortest delegates to the longest.


什么时候不该重载 / When Not to Overload

重载不是越多越好。如果两个函数做的事明显不同,就不要用同名。例如:

Overloading is not about count. If two functions do clearly different things, do not share a name:

// ✗ 名同,语义不同,读起来很累
void send(string msg)             { /* 发广播 */ }
void send(player p, string msg)   { /* 给玩家发私信 */ }

// ✓ 换名字
void broadcast(string msg)        { /* 发广播 */ }
void tell(player p, string msg)   { /* 给玩家发私信 */ }

重载适合"同一个操作针对不同数据"(例如 max 对 int 和 double),而不是"完全不同的两个操作"。

Overload for "same operation on different data" (like max for int and double), not for "two different operations".


小结 / Summary

  • 重载 = 同名、参数列表不同。

  • 返回类型不参与判断。

  • 解析顺序:个数 → 类型 → 有类型声明的优先。

  • 类型匹配是"只宽不窄"。

  • 歧义会报错,需要显式转换。

  • 没有默认参数,用重载模拟。

  • 语义不同的操作别重载。

  • Overloading = same name, different parameter lists.

  • Return type does not count.

  • Resolution: count → type → declared-type preference.

  • Type matching is "widen only".

  • Ambiguity errors; convert explicitly.

  • No default parameters; simulate with overloads.

  • Do not overload semantically different operations.


下一步 / Next Steps

下一章 [Lambda 表达式 / Lambda Expressions] 介绍一种把"代码块"当值传递的写法,GUI 回调、事件处理器都会用到。

Next, [Lambda 表达式 / Lambda Expressions] introduces passing a code block as a value — used by GUI callbacks and event handlers.