重载解决方案

在调用 p(args) 中,选择最匹配的例程 p 。如果多个例程匹配相同,则在语义分析期间报告歧义。

args 中的每个 arg 都需要匹配。一个实参可以匹配多个不同的类别。假设 f 是形参类型,a 是实参类型。

  1. 完全匹配: a 和 f 是同一类型。
  2. 字面值匹配: a 是值 v 的整数字面值, f 是有符号或无符号整数类型, v 在 f 的范围内。 或者: a 是值 v 的浮点字面值, f 是浮点类型, v 在 f 的范围内。
  3. 泛型匹配: f 是泛型类型和 a 匹配,例如 a 是 int 而 f 是泛型(受约束的)参数类型(如在 [T] 或 [ T:int|char] )。
  4. 子范围或子类型匹配: a 是 range[T] , T 与 f 完全匹配。 或者: a 是 f 的子类型。
  5. 整数转换匹配: a 可以转换为 f , f 和 a 是某些整数或浮点类型。
  6. 转换匹配: a 可转换为 f ,可能通过用户定义的 converter 。

选择最佳匹配候选人主要有两种方法,计数和消歧。计数优先于消歧。 在计数中,每个参数都被赋予一个类别,并计算每个类别中的参数数量。 这些类别按优先级顺序列在上面。例如,如果一个具有一个完全匹配的候选人与一个具有多个通用匹配和零个完全匹配的候选人进行比较,那么具有完全匹配的候选人将获胜。

在以下部分中, count(p, m) 计算了对于程序 p ,匹配类别 m 的匹配数量。

如果以下算法返回 true ,则程序 p 的匹配度优于程序 q :

  1. for each matching category m in ["exact match", "literal match",
  2. "generic match", "subtype match",
  3. "integral match", "conversion match"]:
  4. if count(p, m) > count(q, m): return true
  5. elif count(p, m) == count(q, m):
  6. discard "continue with next category m"
  7. else:
  8. return false
  9. return "ambiguous"

当计数产生歧义时,开始进行消歧。参数按位置进行迭代,并比较这些参数对的类型关系。 这种比较的一般目标是确定哪个参数更具体。考虑的类型不是来自调用点的输入,而是竞争候选人的参数类型。

一些例子:

  1. proc takesInt(x: int) = echo "int"
  2. proc takesInt[T](x: T) = echo "T"
  3. proc takesInt(x: int16) = echo "int16"
  4. takesInt(4) # "int"
  5. var x: int32
  6. takesInt(x) # "T"
  7. var y: int16
  8. takesInt(y) # "int16"
  9. var z: range[0..4] = 0
  10. takesInt(z) # "T"

如果这个算法返回 “ambiguous” “歧义”,则执行进一步消除歧义: 如果参数 a 通过子类型关系同时匹配 p 的参数类型 f 和 q 的 g,则考虑继承深度:

  1. type
  2. A = object of RootObj
  3. B = object of A
  4. C = object of B
  5. proc p(obj: A) =
  6. echo "A"
  7. proc p(obj: B) =
  8. echo "B"
  9. var c = C()
  10. # 没有歧义, 调用 'B' ,而不是 'A' ,因为 B 是 A 的子类型
  11. # 但反之亦然:
  12. p(c)
  13. proc pp(obj: A, obj2: B) = echo "A B"
  14. proc pp(obj: B, obj2: A) = echo "B A"
  15. # 但是这个有歧义:
  16. pp(c, c)

类似,对于泛型匹配,最特化的泛型类型(仍然匹配)是首选:

  1. proc gen[T](x: ref ref T) = echo "ref ref T"
  2. proc gen[T](x: ref T) = echo "ref T"
  3. proc gen[T](x: T) = echo "T"
  4. var ri: ref int
  5. gen(ri) # "ref T"

基于 ‘var T’ 的重载

如果形参 f 是 var T 类型,并且进行普通类型检查,参数会被检查为 l-value “左值” 。var T 比 T 更好匹配。

  1. proc sayHi(x: int): string =
  2. # 匹配一个非可变整型
  3. result = $x
  4. proc sayHi(x: var int): string =
  5. # 匹配一个整形变量
  6. result = $(x + 10)
  7. proc sayHello(x: int) =
  8. var m = x # 一个x的可变版本
  9. echo sayHi(x) # 匹配 sayHi 的非可变版本
  10. echo sayHi(m) # 匹配 sayHi 的可变版本
  11. sayHello(3) # 3
  12. # 13

untyped 惰性类型解析

注意: unresolved “未解析”表达式是没有标识符的表达式,不执行查找和类型检查。

因为没有声明为 immediate 的模板和宏会参与重载解析,因此必须有一种方法将未解析的表达式传递给模板或宏。 这就是元类型 untyped 的任务:

  1. template rem(x: untyped) = discard
  2. rem unresolvedExpression(undeclaredIdentifier)

untyped 类型的参数总是匹配任意参数(只要有任意参数传递给它)。

但须小心,因为其他重载可能会触发参数解析:

  1. template rem(x: untyped) = discard
  2. proc rem[T](x: T) = discard
  3. # 未声明的标识符: 'unresolvedExpression'
  4. rem unresolvedExpression(undeclaredIdentifier)

untyped 和 varargs[untyped] 是唯一在这个意义上惰性的元类型,其他元类型 typed 和 typedesc 是非惰性的。

可变参数匹配

参阅 Varargs

迭代器

yielding 类型 T 的迭代器可以通过类型为 untyped (用于未解析的表达式)或类型类 iterable 或 iterable[T] (在类型检查和重载解析之后)的参数传递给模板或宏。

  1. iterator iota(n: int): int =
  2. for i in 0..<n: yield i
  3. template toSeq2[T](a: iterable[T]): seq[T] =
  4. var ret: seq[T]
  5. assert a.typeof is T
  6. for ai in a: ret.add ai
  7. ret
  8. assert iota(3).toSeq2 == @[0, 1, 2]
  9. assert toSeq2(5..7) == @[5, 6, 7]
  10. assert not compiles(toSeq2(@[1,2])) # seq[int] is not an iterable
  11. assert toSeq2(items(@[1,2])) == @[1, 2] # but items(@[1,2]) is