D 函数返回自动引用

D functions returning auto ref

函数上的 return 类型自动引用是什么意思?我只是不理解它。我从 Ali Çehreli 的在线书籍中拿了一个例子,并尝试以各种方式对其进行改编,并查看了 GDC 生成的汇编器,但我仍然没有真正变得聪明。 (我是一位非常有经验的 asm 和 C 程序员,但刚接触 D。)您什么时候真正需要使用此功能?

auto return 函数的主要用例是在模板中,其中函数体根据某些编译时参数发生变化。 auto ref 只是将其扩展为也允许 ref return。看:

auto ref foo(string magic)() {
   static if(magic == "use ref") {
     int* x = new int;
     return *x;
   } else {
     return 0;
   }
}

void main() {
        foo!""();
        foo!"use ref"();
}

每组独特的编译时参数将生成不同的函数。这些不同的函数有完全不同的代码。

这是带有空字符串的函数:

Disassembly of section .text._D3iii15__T3fooVAyaa0_Z3fooFNaNbNiNfZi:

00000000 <_D3iii15__T3fooVAyaa0_Z3fooFNaNbNiNfZi>:
   0:   31 c0                   xor    eax,eax
   2:   c3                      ret
   3:   90                      nop
   4:   90                      nop
   5:   90                      nop
   6:   90                      nop
   7:   90                      nop

如您所见,这是最微不足道的 - return 0。那里没有花哨的 ref return。这是 use ref:

Disassembly of section .text._D3iii29__T3fooVAyaa7_75736520726566Z3fooFNaNbNcNfZi:

00000000 <_D3iii29__T3fooVAyaa7_75736520726566Z3fooFNaNbNcNfZi>:
   0:   55                      push   ebp
   1:   8b ec                   mov    ebp,esp
   3:   b8 00 00 00 00          mov    eax,0x0
   8:   50                      push   eax
   9:   e8 fc ff ff ff          call   a <_D3iii29__T3fooVAyaa7_75736520726566Z3fooFNaNbNcNfZi+0xa>
   e:   83 c4 04                add    esp,0x4
  11:   5d                      pop    ebp
  12:   c3                      ret
  13:   90                      nop
  14:   90                      nop
  15:   90                      nop
  16:   90                      nop
  17:   90                      nop

(损坏的名称中包含字符串作为 ascii 的十六进制字符串(好吧,它实际上是 UTF-8,unicode 也可以!)字节顺便说一句,75736520726566 部分 - 将 0x75 识别为 'u' 和 0x66 为 'f'。D 损坏的名称可能会长得离谱,但一旦你了解它,该模式就非常容易阅读。)

该代码调用 new,因此主体更复杂,但请注意 eax 仍然保存着 return 值……指针。现在,让我们从 auto ref 中取出 ref 并重新编译相同的东西。

第一个功能当然还是一样。第二个改变了:

Disassembly of section .text._D3iii29__T3fooVAyaa7_75736520726566Z3fooFNaNbNfZi:

00000000 <_D3iii29__T3fooVAyaa7_75736520726566Z3fooFNaNbNfZi>:
   0:   55                      push   ebp
   1:   8b ec                   mov    ebp,esp
   3:   b8 00 00 00 00          mov    eax,0x0
   8:   50                      push   eax
   9:   e8 fc ff ff ff          call   a<_D3iii29__T3fooVAyaa7_75736520726566Z3fooFNaNbNfZi+0xa>
   e:   8b 00                   mov    eax,DWORD PTR [eax]
  10:   83 c4 04                add    esp,0x4
  13:   5d                      pop    ebp
  14:   c3                      ret
  15:   90                      nop
  16:   90                      nop
  17:   90                      nop

看到 mov eax,DWORD PTR [eax] 了吗?它不再 returning 指针(当然,这就是 ref return 在幕后所做的),但现在它指向的是 - 一个正常的,非引用, 值 return.

这就是autoreturn和auto ref的区别。

为了完整起见,我们将其更改为 ref int,看看会发生什么:

$ dmd iii
iii.d(6): Error: constant 0 is not an lvalue
iii.d(11): Error: template instance iii.foo!"" error instantiating

现在 return 0 是错误的,因为那不是有效的 ref

实际用例是,模板可能会根据各种参数做出更复杂的决定,决定在它们的主体中做什么,从而决定它们的 return 类型。它可能只是 return 传递给它的参数的任何类型,它可能会检查平台版本(那将是一些丑陋的代码大声笑),无论如何。 auto ref 允许您涵盖各种类似的情况,而无需自己编写单独的函数签名(以及函数体)。

auto 用于当您希望编译器确定函数将 return 的类型时。例如,下面两个函数是相同的:

uint returnAUint(){
   uint r=256;
   return r;
}
auto function2(){
   uint r=256;
   return r;
}

ref意味着函数将return一个pointer/reference。所以 auto ref 是一个函数,它将 return 一个指向编译器必须自行识别的数据类型的指针。