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

Reply via email to