I have been working on a class-based finite random access range. When performing a few tests on it:
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
I got this compilation error in GDC 4.9.2, regarding the last line in the snippet above: "algorithm.d|4838|error: foreach: cannot make e ref"
The error points to this piece of code in std.algorithm.find
(the find_if variant, taking a range and a predicate), which indeed takes a reference to each element with 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
{
...
}
}
This most likely happens because I have provided an implementation of opApply
that does not provide a ref
argument (neither does the class provide a ref
return type to any other member function).
int opApply(int delegate(E) f) {...}
int opApply(int delegate(size_t,E) f) {...}
I could change that, but what really bothers me is that right now the range class complies to the preconditions of the function, and foreach
iteration should still work with them anyway. Quoting from the documentation:
Iteration over struct and class objects can be done with ranges. For
foreach
, this means the following properties and methods must be defined:Properties:
.empty
returns true if no more elements.front
return the leftmost element of the rangeMethods:
.popFront()
move the left edge of the range right by one
All these were provided (otherwise it wouldn't be a random access range), so it should use them. Instead, it might be looking for the alternate iteration method described next:
If the aggregate expression is a struct or class object, and the range properties do not exist, then the foreach is defined by the special
opApply
member function and the foreach_reverse behavior is defined by the specialopApplyReverse
member function. These functions have the type:
int opApply(int delegate(ref Type [, ...]) dg);
Which to my interpretation, shouldn't have been looked for.
Also quoting std.algorithm.all
, which does not seem to demand iteration for references either:
bool all(Range)(Range range) if (isInputRange!Range && is(typeof(unaryFun!pred(range.front))));
Returns true if and only if all values v found in the input range range satisfy the predicate pred. Performs (at most) Ο(range.length) evaluations of pred.
So is this a bug in the Phobos library, and std.algorithm.find
should iterate by value in the first place? Or is there something that I have missed?