理解D语言中的引用

aka*_*nuk 2 d return ref

说,我想添加两个数字的向量(在数学意义上).我自然会做类似的事情:

T[] add(T)(T[] a, T[] b) {
    assert(a.length == b.length);
    T[] res = a.dup;
    foreach (i; 0 .. a.length) {
        res[i] = a[i] + b[i];
    }
   return res;
}
Run Code Online (Sandbox Code Playgroud)

嗯,没关系,但我怀疑ab复制每一个电话,这不是很好.所以我宣布他们ref.

T[] add(T)(ref T[] a, ref T[] b) { ...
Run Code Online (Sandbox Code Playgroud)

它在传递变量时效果很好,但对于测试,我使用数组实例:

assert(add([1, 2, 3], [4, 5, 6]) == [5, 7, 9]);
Run Code Online (Sandbox Code Playgroud)

这失败了,因为它无法推断出数组的引用.我已经设法找到一个解决方法:

T[] add(T)(T[] a, T[] b) {
    return add(a, b);
}
Run Code Online (Sandbox Code Playgroud)

这似乎解决了这个问题,但看起来相当愚蠢.什么是我的问题更好的设计?

或者把它放在较小的问题中:我是否真的必须声明参数ref以避免复制?可以编译器,因为我不修改,ab为我优化这个?如何通过方式声明参数不可变(我尝试过immutable关键字,看起来我使用它错了)?将res在变通方法中真正复制两次,还是通过移动返回?

Jon*_*vis 5

你真的应该阅读http://dlang.org/d-array-article.html.它详细介绍了D中的数组是如何工作的.但是对于简短的回答,所有在传递参数时都会被复制

T[] add(T)(T[] a, T[] b) {...}
Run Code Online (Sandbox Code Playgroud)

指针的底层指针和长度.没有复制任何元素.相反,阵列是"切片".结果内部的数组addadd参数的片段,这意味着它们引用的是与原始数组相同的内存.改变切片的元素将改变原始数组的元素,因为它们是相同的元素.但是,改变数组本身(例如,为其分配另一个数组或附加到它)不会影响原始数据,如果附加到数组会导致其内存被重新分配以腾出空间(或者如果为阵列分配了新数组) ),然后该数组将不再引用与原始数据相同的元素.代码中唯一一个正在制作数组副本的地方a.dup.

数组标记的ref作用是使它们不被切片.相反,它们原始数组而不是切片.因此,如果有任何内容附加到本地数组或者它被重新分配,那么这将影响原始数组(如果你没有使用它将不会有ref).

此外,ref只接受左值(意味着值可以在赋值的左侧),并且数组文字是右值(意味着它们只能在赋值的右侧),所以你不能将它们传递给一个接受其参数的函数ref.如果你想同时接受这两个,你要么必须不接受ref,重载你的功能,以便你有一个refref版本(看起来你用作你的解决方案),或者使用auto ref而不是ref,在这种情况下它将接受两者(但auto ref仅适用于模板化函数,并且它基本上只是用于自己复制函数的简写,因为这是什么auto ref一样).一般来说,如果你不想改变原作,你不应该路过ref.

一个建议让你的代码更快:没有理由dup a然后再循环它并添加它b.如果这就是你想要做的事情,你也可以只使用+=并做更多的事情

T[] add(T)(T[] a, T[] b)
{
    assert(a.length == b.length);
    auto res = a.dup;
    foreach (i; 0 .. a.length)
        res[i] += b[i];
   return res;
}
Run Code Online (Sandbox Code Playgroud)

或者甚至更好,您可以使用数组向量操作并完全跳过循环:

T[] add(T)(T[] a, T[] b)
{
    assert(a.length == b.length);
    auto res = a.dup;
    res[] += b[];
    return res;
}
Run Code Online (Sandbox Code Playgroud)

但是,如果你想正确理解数组如何在D中工作,你真的应该阅读http://dlang.org/d-array-article.html.

  • 不是'res [] + = b [];`也可以消除循环? (3认同)