typedesc[T]

在一些上下文中,Nim 把类型名当作常规的值处理。这些值只存在于编译阶段,由于所有的值都必须有类型, 就用 typedesc 来表示它们的这种特殊类型。

typedesc 作为泛型类型。例如,标识符 int 的类型是 typedesc[int] 。 和普通的泛型一样,当省略泛型参数时,typedesc 就表示所有的类型类。 作为一种语法上的便利,我们也可以使用 typedesc 作为修饰语。

具有 typedesc 参数的过程,被当成是隐式泛型的。 它们按提供类型的每个特定组合来实例化,并在过程主体中,每个参数名称将指代为绑定的具体类型。

  1. proc new(T: typedesc): ref T =
  2. echo "allocating ", T.name
  3. new(result)
  4. var n = Node.new
  5. var tree = new(BinaryTree[int])

当出现多个类型参数时,它们将自由绑定到不同的类型。可以使用明确的泛型参数,来强制执行一次性绑定。

  1. proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U])

一旦绑定,类型参数就可以出现在过程签名的其它部分:

  1. template declareVariableWithType(T: typedesc, value: T) =
  2. var x: T = value
  3. declareVariableWithType int, 42

通过限制与类型参数相匹配的类型集,可以进一步影响重载解析。在实践中,通过模板将属性附加到类型上。该约束可以是一个具体的类型或一个类型类。

  1. template maxval(T: typedesc[int]): int = high(int)
  2. template maxval(T: typedesc[float]): float = Inf
  3. var i = int.maxval
  4. var f = float.maxval
  5. when false:
  6. var s = string.maxval # error, maxval is not implemented for string
  7. template isNumber(t: typedesc[object]): string = "不这么看。"
  8. template isNumber(t: typedesc[SomeInteger]): string = "是的!"
  9. template isNumber(t: typedesc[SomeFloat]): string = "有可能,也可能是 NaN。"
  10. echo "int 是数字吗? ", isNumber(int)
  11. echo "float 是数字吗? ", isNumber(float)
  12. echo "RootObj 是数字吗? ", isNumber(RootObj)

给宏传入 typedesc 与传入其它参数几乎是一样的,区别仅在于宏一般不会被实例化。类型表达式简单地作为 NimNode 传给宏,就像其它任何东西一样。

  1. import std/macros
  2. macro forwardType(arg: typedesc): typedesc =
  3. # `arg` 的类型是 `NimNode`
  4. let tmp: NimNode = arg
  5. result = tmp
  6. var tmp: forwardType(int)