On Sunday, 17 October 2021 at 22:52:27 UTC, Elmar wrote:
> Hello Dear community.
I'd like to overload opIndexAssign
for a struct which wraps around a generic array (so that it can't support opIndex
due to unknown return type).
Broken down as much as possible this is the code:
import std.stdio : writeln;
import std.range : ElementType;
struct S {
void opIndexAssign(X, RANGE)(X x, RANGE range)
if (is(ElementType!RANGE : size_t))
{
writeln(__FUNCTION__);
}
auto opSlice(size_t start, size_t end) {
import std.range : iota;
return iota(start, end);
}
}
void main()
{
auto arr = new int[7];
S s;
s.opIndexAssign(arr, s.opSlice(1,4)); // works
s[0..3] = arr[1..4]; // does not work, compiles to `s.opSlice(0,3) = arr[1..4]`
}
I'm clueless about why it wouldn't compile the last statement to s.opIndexAssign(arr[1..4], s.opSlice(0,3))
.
Help appreciated :-)
What happens here is, the compiler first tries the D2-style rewrite:
s.opIndexAssign(arr[1..4], s.opSlice!0(0, 3))
However, that rewrite fails to compile, because your opSlice
does not take a template argument specifying the dimension along which to slice, as specified in the language spec's section on "Slice Operator Overloading".
Since the above rewrite fails to compile, it falls back to rewriting the expression using D1-style operator overloads:
> For backward compatibility, a[]
and a[i..j]
can also be overloaded by implementing opSlice()
with no arguments and opSlice(i, j)
with two arguments, respectively. This only applies for one-dimensional slicing, and dates from when D did not have full support for multidimensional arrays. This usage of opSlice is discouraged.
...which results in the following rewritten statement:
s.opSlice(0, 3) = arr[1..4];
My guess is that you got into this situation by trying to follow the example in the spec's section on "Slice Assignment Operator Overloading". Unfortunately, that example is incorrect.
Here is a fixed version of your example on run.dlang.io: https://run.dlang.io/is/dtfT5y