std.algorithm.find是否需要范围元素的引用?

std.algorithm.find是否需要范围元素的引用?,d,phobos,D,Phobos,我一直在研究基于类的有限随机访问范围。对其执行一些测试时: 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

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

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
我在GDC4.9.2中得到了这个编译错误,关于上面代码段的最后一行:“algorithm.d | 4838 | error:foreach:cannotmakee 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
    {
       ...
    }
}
我可以改变这一点,但真正困扰我的是,现在range类符合函数的先决条件,
foreach
iteration仍然应该使用它们。引用文件:

对结构和类对象的迭代可以通过范围来完成。对于
foreach
,这意味着必须定义以下属性和方法:

特性:

  • .empty
    如果没有更多元素,则返回true
  • .front
    返回范围中最左边的元素
方法:

  • .popFront()
    将范围的左边缘向右移动一步
所有这些都是提供的(否则它就不是一个随机访问范围),所以它应该使用它们。相反,它可能在寻找下面描述的替代迭代方法:

如果聚合表达式是结构或类对象,且范围属性不存在,则foreach由特殊的
opApply
成员函数定义,foreach_反向行为由特殊的
opApplyReverse
成员函数定义。这些功能具有以下类型:

int-opApply(int-delegate(ref类型[,…])dg)

根据我的理解,这是不应该被寻找的

还引用了
std.algorithm.all
,这似乎也不需要迭代引用:

bool all(Range)(Range-Range)if(isInputRange!Range&&is(typeof(unaryFun!pred(Range.front)))

当且仅当 在输入范围中找到的所有值v都满足谓词 pred。对pred执行(最多)Б(范围.长度)评估


那么这是不是Phobos库中的一个bug,
std.algorithm.find
首先应该按值进行迭代?或者我遗漏了什么?

在一个应该是范围的对象上声明
opApply
是没有意义的,因为如果它是范围,那么基于范围的函数将用于
foreach
,而不是
opApply
。当然,如果对范围类型调用的是
opApply
,而不是
front
popFront
empty
,那么这就是编译器错误。从声音上看,编译器错误地选择了
opApply
,因为
opApply
使用了
ref
,而
front
没有。但是,只要未声明
opApply
,使用
ref
foreach
front
就可以正常工作。因此,
ref
与其说是一个问题,不如说是编译器在看到
opApply
ref
front
没有时错误地使用了
opApply

因此,需要修复编译器,但这可能以前从未被发现,因为像您这样在范围类型上声明
opApply
是没有意义的。因此,我认为您的代码需要更改为不在范围类型上声明
opApply
。这样你就不会碰到这个特殊的错误了

也就是说,Phobos中讨论的代码对于引用类型(如类)的范围来说是有缺陷的,因为它在
haystack
上迭代时无法调用
save
。其结果是原始范围发生了变异,以引用正在搜索的点,而返回的点远超过了正确的点,就像元素来自干草堆前面一样。因此,即使您停止声明
opApply
和/或编译器错误得到修复,如果您使用范围的引用类型,std.algorithm.find也需要修复,代码才能开始工作

编辑:

好的。那不太对。在与一些编译器开发人员讨论时,我得到了纠正。过去,范围函数优于
opApply
,这就是规范所说的,但在某些时候,它被更改为
opApply
优于范围函数,因此范围类型可以使用
opApply
foreach
进行迭代,如果这对它更有效的话(虽然这显然会带来范围函数和
opApply
不具有相同行为的风险,这可能会导致一些非常严重的错误)。因此,规范与编译器的当前行为不匹配,您可以在范围类型上声明
opApply
(尽管我仍然建议不要这样做,除非你从中获得了明确的性能增益)

也就是说,这里出现错误的事实仍然是一个编译器错误。因为您的
opApply
没有使用
ref
,它不能与
ref
循环变量一起工作,而范围函数会,因此在这种情况下,编译器应该调用范围函数,显然不是。无论如何,这都是错误的“以前没有发现过,因为几乎没有人在范围上使用
opApply
,因为这样做的唯一原因是这样做是否会提高性能,而且我确信,事实上规范仍然说范围函数比
opApply
更受欢迎,这使得尝试过它的人比ca更少se.

的确,那是奈尔
int opApply(int delegate(E) f) {...}
int opApply(int delegate(size_t,E) f) {...}