d - std.algorithm.find 是否要求引用范围元素



我一直在研究基于类的有限随机访问范围。对其执行一些测试时:

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是在范围类型而不是frontpopFrontempty上调用的,那么这是一个编译器错误。从它的声音来看,编译器错误地选择了opApply,因为opApply使用ref,而front则没有。但是,只要未声明opApply,该front就可以正常工作,而无需与使用refforeach ref。因此,ref与其说是编译器在看到opApplyreffront没有时错误地使用opApply,不如说是一个问题。

因此,编译器需要修复,但这以前可能从未被捕获过,因为像您正在做的那样在范围类型上声明opApply是没有意义的。因此,我认为您的代码需要更改为不声明范围类型的opApply。那么你甚至不会遇到这个特定的错误。

话虽如此,Phobos 中有问题的代码对于引用类型(如类)的范围来说是错误的,因为它在迭代它时无法在haystack上调用save。这样做的结果是,原始范围被改变为引用正在搜索的位置,而返回的内容指向远远超过正确的位置,因为元素来自大海捞针的前面。因此,即使您停止声明opApply和/或编译器错误得到修复,如果您对该范围使用引用类型,也需要修复 std.algorithm.find 才能开始工作。

编辑:

好。这不太对。在与一些编译器开发人员讨论时,我已经得到了纠正。过去是范围函数比opApply更受青睐,这就是规范所说的,但在某些时候,它被更改了,以便opApply比范围函数更受青睐,以便范围类型可以使用foreach opApply进行迭代,如果这对它更有效(尽管这显然引入了范围函数的风险,并且opApply没有相同的行为, 这可能会导致一些非常讨厌的错误)。因此,该规范与编译器的当前行为不匹配,它应该适用于您在范围类型上声明opApply(尽管我仍然建议不要这样做,除非您从中获得明确的性能提升)。

话虽如此,您在此处收到错误的事实仍然是编译器错误。由于您的opApply不使用 ref ,它不适用于 ref 循环变量,而范围函数会,因此在这种情况下编译器应该调用范围函数,显然不是。无论哪种方式,这在以前都没有被发现,因为几乎没有人在范围上使用opApply,因为这样做的唯一原因是这样做是否有性能提升,而且我确信规范仍然说范围函数优于opApply的事实使得尝试过它的人比其他情况更少。

相关内容

  • 没有找到相关文章

最新更新