我一直在研究基于类的有限随机访问范围。对其执行一些测试时:
auto myRange = /* construct my range */
static assert (isRandomAccessRange!(typeof(myRange))); //
static assert (!isInfinite!(typeof(myRange))); // both pass
auto preamble = myRange[0..128];
assert( all!"a == 0"(preamble)); // check for all zeros
我在GDC 4.9.2中得到了这个编译错误,关于上面代码片段的最后一行:"algorithm.d|4838|error:foreach:无法进行e ref"
错误指向 std.algorithm.find
中的这段代码(find_if变体,采用范围和谓词),它确实引用了每个元素,foreach
:
InputRange find(alias pred, InputRange)(InputRange haystack)
if (isInputRange!InputRange)
{
alias R = InputRange;
alias predFun = unaryFun!pred;
static if (isNarrowString!R)
{
...
}
else static if (!isInfinite!R && hasSlicing!R && is(typeof(haystack[cast(size_t)0 .. $])))
{
size_t i = 0;
foreach (ref e; haystack) // <-- needs a ref
{
if (predFun(e))
return haystack[i .. $];
++i;
}
return haystack[$ .. $];
}
else
{
...
}
}
发生这种情况很可能是因为我提供了不提供ref
参数的opApply
实现(该类也没有为任何其他成员函数提供ref
返回类型)。
int opApply(int delegate(E) f) {...}
int opApply(int delegate(size_t,E) f) {...}
我可以改变这一点,但真正困扰我的是,现在范围类符合函数的先决条件,无论如何foreach
迭代仍然应该使用它们。引用文档:
结构和类对象的迭代可以使用范围完成。对于
foreach
,这意味着必须定义以下属性和方法:性能:
- 如果没有更多元素,则返回
.empty
true.front
返回范围最左侧的元素方法:
.popFront()
将范围的左边缘向右移动一个
所有这些都是提供的(否则它不会是随机访问范围),所以它应该使用它们。相反,它可能正在寻找下面描述的备用迭代方法:
如果聚合表达式是结构或类对象,并且范围属性不存在,则 foreach 由特殊
opApply
成员函数定义,foreach_reverse行为由特殊opApplyReverse
成员函数定义。这些函数的类型如下:
int opApply(int delegate(ref Type [, ...]) dg);
根据我的解释,不应该寻找。
还引用std.algorithm.all
,这似乎也不需要迭代引用:
bool all(Range)(Range range) if (isInputRange!Range && is(typeof(unaryFun!pred(range.front))));
返回 true 当且仅当 在输入范围范围内找到的所有值 V 都满足谓词 捕食。对pred执行(最多)Ο(范围长度)评估。
那么这是火卫一库中的一个错误吗,std.algorithm.find
首先应该按值迭代?还是我错过了什么?
在一个应该是范围的对象上声明opApply
甚至没有意义,因为如果它是一个范围,那么基于范围的函数将用于foreach
,而不是opApply
。当然,如果opApply
是在范围类型而不是front
、popFront
和empty
上调用的,那么这是一个编译器错误。从它的声音来看,编译器错误地选择了opApply
,因为opApply
使用ref
,而front
则没有。但是,只要未声明opApply
,该front
就可以正常工作,而无需与使用ref
的foreach
ref
。因此,ref
与其说是编译器在看到opApply
有ref
而front
没有时错误地使用opApply
,不如说是一个问题。
因此,编译器需要修复,但这以前可能从未被捕获过,因为像您正在做的那样在范围类型上声明opApply
是没有意义的。因此,我认为您的代码需要更改为不声明范围类型的opApply
。那么你甚至不会遇到这个特定的错误。
话虽如此,Phobos 中有问题的代码对于引用类型(如类)的范围来说是错误的,因为它在迭代它时无法在haystack
上调用save
。这样做的结果是,原始范围被改变为引用正在搜索的位置,而返回的内容指向远远超过正确的位置,因为元素来自大海捞针的前面。因此,即使您停止声明opApply
和/或编译器错误得到修复,如果您对该范围使用引用类型,也需要修复 std.algorithm.find 才能开始工作。
编辑:
好。这不太对。在与一些编译器开发人员讨论时,我已经得到了纠正。过去是范围函数比opApply
更受青睐,这就是规范所说的,但在某些时候,它被更改了,以便opApply
比范围函数更受青睐,以便范围类型可以使用foreach
opApply
进行迭代,如果这对它更有效(尽管这显然引入了范围函数的风险,并且opApply
没有相同的行为, 这可能会导致一些非常讨厌的错误)。因此,该规范与编译器的当前行为不匹配,它应该适用于您在范围类型上声明opApply
(尽管我仍然建议不要这样做,除非您从中获得明确的性能提升)。
话虽如此,您在此处收到错误的事实仍然是编译器错误。由于您的opApply
不使用 ref
,它不适用于 ref
循环变量,而范围函数会,因此在这种情况下编译器应该调用范围函数,显然不是。无论哪种方式,这在以前都没有被发现,因为几乎没有人在范围上使用opApply
,因为这样做的唯一原因是这样做是否有性能提升,而且我确信规范仍然说范围函数优于opApply
的事实使得尝试过它的人比其他情况更少。