On 15/04/18 09:39, Jonathan M Davis wrote:
It's extremely common for range-based functions to copy front. Even foreach
does it. e.g.
Not necessarily:
foreach(ref e; [S(0), S(1), S(2)]){}
While that would work with foreach, it will not work with anything that
expects an inputRange (and since D defines a forward range as an
extension, this is also not a forward range).
If non-copyable types were allowed, then no function which accepted a range
would be allowed to copy front without first checking that front was
copyable (something that most code simply isn't going to do)
No, that's not correct.
If non-copyable types were allowed, then functions that accept a range
would fail to compile on non-copyable types if they do a copy.
But the flip side is that if non-copyable would be allowed, a lot of
functions that current copy would not. There are far too many
unnecessary copies in the code that serve no purpose, but they are
invisible, so they are not fixed.
Remember also that a large percentage of ranges that don't wrap dynamic
arrays put their elements on the stack, which means that whenever the range
is copied, the elements are copied.
If I am guaranteed to be able to copy an input range, what's the
difference between it and a forward range?
Strike that, I have a better one: If I am allowed to copy an input
range, what does the "save" function in forward range even mean?
I'm not sure why it ends up printing each of them 3 times rather than 2,
Because some code somewhere does an unnecessary copy?
the fact that foreach copies any range that it's given means that in order
to have ranges allow non-copyable types, we'd have to change how foreach
worked, which would break a lot of code. Right now,
Like I said above, foreach(ref) works with "input ranges" that define
ref return from front to uncopyable objects. Foreach without ref
doesn't, but that's mandatory from the interface: You cannot iterate
copies of a range returning uncopyable objects.
As for ranges that store the values inside them: you REALLY don't want
to copy those around. They may get quite big (not to mention that I
don't see how you can define one over a non-copyable type).
foreach(e; range)
{
}
is lowered to something like
for(auto __range = range; !__range.empty; __range.popFront())
{
auto e = __range.front;
}
And that indeed generates a lot of confusion regarding what running two
foreaches in a row does. This is a bug that already affects more than
just uncopyable objects.
That requires both copying the range and copying front. We could
theoretically change it so that everywhere that e was used, it would be
replaced with __range.front
Like I said, already solved for foreach(ref)
Now, generic range-based code really shouldn't ever use a range after it's
been copied, since whether mutating the copy affects the original depends on
the implementation of the range (and thus generic code should assume that
foreach may have consumed the range and call save if it doesn't want that to
happen), but lots of code does it anyway,
And allowing ranges with uncopyable members would turn this potential
deadly run-time bugs into easy to find compile time errors. Sound like a
great reason to allow it, to me.
and if the code isn't generic, it
can work just fine, since then the code can depend on the semantics of that
specific range type
Such as it being copyable? Non-generic code is of no relevance to this
discussion, as far as I can tell.
Also, in order for a range to work with a non-copyable type, either front
would have to return by ref or it would have to construct a new object to
return so that it could be moved rather than copied.
Correct
I expect that quite a
few ranges would have problems with such a restriction,
Then those ranges will not work with non-copyable objects. That's no
reason to make it impossible for *any* range to work with non-copyable.
In addition, it's quite possible that forcing functions to not copy front
would hurt performance.
I'm going to stop responding now, because, frankly, I think you and I
are having very different discussions.
You seem to be advocating against making *all* ranges support
non-copyable types, while I'm merely asking that *any* range be allowed
to support such types.
Current situation is that a range with uncopyable types is not
considered an input range, and cannot use any phobos range functions,
including some that it should be able to use without a problem.
There is no reason to block such ranges from using "map" or "any",
except the fact they require that the type answer "isInputRange" with true.
IIRC, when this has come up previously, Andrei ruled that supporting
non-copyable types simply wasn't worth the extra complication, though a
quick search doesn't turn up anything where he talked about it. So, I can't
verify that at the moment.
I will trust Andrei to weigh in on this point if he thinks he should.
Until he does, please feel free to only bring up points you are willing
to argue yourself.
Shachar