具名返回值优化 (NRVO)

注意: 本节文档仅描述当前的实现。这部分语言规范将会有变动。 详情查看链接 https://github.com/nim-lang/RFCs/issues/230

在例程内部返回值以特殊的 result 变量出现。这为实现与 C++ 的 “具名返回值优化” (NRVO) 类似的机制创造了条件。 NRVO 指的是 p 内对 result 的操作会直接影响 let/var dest = p(args) (定义 dest) 或 dest = p(args) (给 dest 赋值) 中的目标 dest 。 这是通过将 dest = p(args) 重写为 p’(args, dest) 来实现的,其中 p’ 是 p 的变体,它返回 void 并且接收一个与 result 对应的可变参数。

不太正式的示例:

  1. proc p(): BigT = ...
  2. var x = p()
  3. x = p()
  4. # 上面这段代码大致上会被解释为以下代码
  5. proc p(result: var BigT) = ...
  6. var x; p(x)
  7. p(x)

假设 p 的返回值类型为 T。 当 sizeof(T) >= N (N 依赖于具体实现) 时,编译器就会使用 NRVO。 换句话说,NRVO 适用于 “较大” 的结构体。

即使 p 可能抛出异常,依然会使用 NRVO。这会带来显著的不同行为:

  1. type
  2. BigT = array[16, int]
  3. proc p(raiseAt: int): BigT =
  4. for i in 0..high(result):
  5. if i == raiseAt: raise newException(ValueError, "interception")
  6. result[i] = i
  7. proc main =
  8. var x: BigT
  9. try:
  10. x = p(8)
  11. except ValueError:
  12. doAssert x == [0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0]
  13. main()

编译器能够检测这些情况并发出警告,但是这个行为默认是关闭的。通过 warning[ObservableStores] 以及 push/pop 编译指示可以为一段代码打开这个警告。以上面的代码为例:

  1. {.push warning[ObservableStores]: on.}
  2. main()
  3. {.pop.}