我一直致力于基于类的有限随机访问范围。在对它进行一些测试时:
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 |错误: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
{
...
}
}
这很可能是因为我提供了opApply
的实现,它没有提供ref
参数(该类也没有为任何其他成员函数提供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))));
当且仅当if时返回true 在输入范围范围内找到的所有值v都满足谓词 预计值。对pred进行(最多)Ο(range.length)评估。
这是Phobos库中的一个错误,std.algorithm.find
应该首先按值迭代吗?或者有什么我错过了吗?
答案 0 :(得分:1)
在一个应该是一个范围的对象上声明opApply
甚至没有意义,因为如果它是一个范围,那么基于范围的函数将用于foreach
,而不是opApply
。当然,如果在范围类型而不是opApply
,front
和popFront
上调用empty
,那么这就是编译器错误。从它的声音来看,编译器错误地选择opApply
,因为opApply
使用ref
,而front
没有。但是,只要未声明front
,ref
只有foreach
ref
使用opApply
,ref
才能正常工作。因此,opApply
不是问题,因为当编译器看到opApply
有ref
而front
没有opApply
时错误地使用了opApply
这一事实“T
所以,编译器需要修复,但这可能永远不会被捕获,因为在你正在做的范围类型上声明save
是没有意义的。因此,我认为您的代码需要更改为不在范围类型上声明haystack
。然后你甚至不会遇到这个特殊的错误。
话虽如此,Phobos 中的代码对于作为引用类型(如类)的范围来说是错误,因为它无法在opApply
上调用opApply
它迭代它。结果是原始范围变异以引用正在搜索的点,而返回的点指向远离正确点的元素来自干草堆的前面。因此,即使您停止声明opApply
和/或编译器错误得到修复,也需要修复std.algorithm.find,以便在您使用范围的引用类型时代码开始工作。 / p>
修改强>
好。那不太对劲。在与一些编译器开发者讨论它时我得到了纠正。过去,范围函数比opApply
更受青睐,这就是规范所说的,但在某些时候,它被改变,因此foreach
比范围函数更受青睐,因此范围类型可以使用opApply
和opApply
进行迭代,如果它对它更有效(尽管这显然会引入范围函数和opApply
没有相同行为的风险,这可能会导致一些真正的令人讨厌的错误)。因此,规范与编译器的当前行为不匹配,并且 应该为你在范围类型上声明ref
(尽管我仍然建议不要使用它,除非你'从中获得明确的性能提升)。
话虽如此,你在这里收到错误的事实仍然是编译错误。由于ref
不使用opApply
,因此它不适用于opApply
循环变量,而范围函数则适用,因此在这种情况下编译器应该调用范围函数,显然不是。无论哪种方式,这都没有被捕获,因为几乎没有人在范围上使用{{1}},因为这样做的唯一原因是如果这样做会有性能提升,而且我确信这个事实是spec仍然表示范围函数比{{1}}更受欢迎,这使得尝试它的人数比其他人少。