On Friday, January 19, 2024 3:49:29 AM MST Jim Balter via Digitalmars-d-learn wrote:On Friday, 17 November 2017 at 17:55:30 UTC, Jonathan M Daviswrote: > When you have > > foreach(e; range) > > it gets lowered to something like > > for(auto r = range; !r.empty; r.popFront()) > { > > auto e = r.front; > > } > > So, the range is copied when you use it in a foreach.Indeed, and the language spec says so, but this is quite wrong asit violates the specification and design of ranges ... onlyforward ranges are copyable and only via their `save` function. I have an input range that can only be iterated once; if you try to do so again it's empty ... but the foreach implementation breaksthat. You should be able to break out of the foreach statement, then run it again (or another foreach) and it should continue from where it left off, but copying breaks that. I need to knowhow many elements of my range were consumed; copying breaks that.I got around this by having a pointer to my state so only thepointer gets copied. I would also note that tutorials such as Ali Çehreli's "Programming in D – Tutorial and Reference" are unawareof this breakage: " Those three member functions must be named as empty, popFront, and front, respectively. The code that is generated by the compiler calls those functions: for ( ; !myObject.empty(); myObject.popFront()) { auto element = myObject.front(); // ... expressions ... } "
"No spec is being violated, and the behavior is completely expected."
This is not true, and your over-long response that is a lecture I didn't ask for and don't need misses my point and is largely irrelevant. The specification of ranges, which is independent of the D language, says that the way to copy a range is to use save(). Input ranges cannot be copied or restarted; that's the whole point of the difference between input ranges and forward ranges. You go on and on about the semantics of copying, which has nothing to do with what I wrote, which is that `foreach` copies when it shouldn't; the semantics of copying is not relevant to *not copying*. Input ranges are one-shots; they yield values once and that's it. `foreach` should have been implemented to call `save()` when that is available (when it is a forward range), and to do no copying when it is not available. Had that been done, then people wouldn't have written a bunch of ranges with missing save() functions that are reusable when they shouldn't be due to `foreach` making a copy, making `foreach` impossible to fix now.
"Basic input ranges cannot be value types (otherwise, they could implement save)"
This is not true, and again misses the point. My input range, the one I described in my original message, is a value type (a struct) which, when passed to functions, is passed by reference. It doesn't implement save because it's an input range, not a forward range. It's not supposed to be copied or restartable because it's an input range, not a forward range. Copying it has the wrong consequences because calling popFront on a copy doesn't advance the one-shot input range. It would work just fine if `foreach` didn't copy it. It works fine if I use it without `foreach`, e.g., if I use it in a for loop like in Ali's book:
for ( ; !myObject.empty(); myObject.popFront()) { auto element = myObject.front(); // ... expressions ... }
That works just fine with an input range, which is a range that is not restartable. The only thing that doesn't work with my input range is `foreach`, which copies the input range--which, by the specification of input ranges, was never intended.
I managed to make it usable with a `foreach` loop by having the struct only store a pointer to its state so the `foreach` copy is harmless, in effect turning my value type range into a reference type and introducing unnecessary overhead.