called copy constructor in foreach with ref on Range

Stanislav Blinov stanislav.blinov at gmail.com
Tue Jun 23 01:31:37 UTC 2020


On Monday, 22 June 2020 at 21:33:08 UTC, H. S. Teoh wrote:

> Don't be shocked when you find out how many Phobos ranges have 
> .init states that are invalid (e.g., non-empty, but .front and 
> .popFront will crash / return invalid values).

Which ones?

> Jonathan is coming from the POV of generic code.  The problem 
> with move leaving the original range in its .init state isn't 
> so much that it will crash or anything (even though as you said 
> that does indicate a flaw in the range's implementation), but 
> that the semantics of generic code changes in subtle ways.

Well that means that the code is not generic, i.e. the bug 
originates in the design, not implementation.

> 	auto myGenericFunc(R)(R r) {
> 		...
> 		foreach (x; r) {
> 			doSomething(x);
> 		}
> 		if (!r.empty)
> 			doSomethingElse(r);
> 		...
> 	}
>
> Suppose for argument's sake that the above foreach/if structure 
> is an essential part of whatever algorithm myGenericFunc is 
> implementing. Now there's a problem, because if R has 
> array-like semantics, then the algorithm will do one thing, but 
> if R has reference-like or move semantics, then the behaviour 
> of the algorithm will be different, even if both ranges 
> represent the same sequence of input values.

Yep, definitely not generic. Which is exactly the kind of error 
that should be caught at compile time. Which we, sadly, can't do 
with the current range API.

> consider a function that drops the first n elements of a range.
> Your generic function might want to pop the first n elements 
> then do something else with the rest of the range.  Well, if 
> you write it the obvious way:
>
> 	auto myAlgo(R)(R r) {
> 		size_t n = ...;
> 		dropFirstN(r, n);
> 		... // do something else with r
> 	}
>
> then you have a subtle bug, because the state of r after the 
> call to dropFirstN might be completely different depending on 
> whether r behaves like an array or like a by-reference or move 
> type.

Err... that one is actually fine. It would take the range by ref 
and pop off the elements. There already is such a function - the 
popFrontN. It's the functions that take ranges by value that 
present the not-so-subtle issue with reference types. For 
example, its chainable would-be-counterpart drop(). "Would-be" 
because that one takes the argument by value.
We should move toward disallowing reference types to be ranges. A 
good deal of the rest can be solved with API and design changes 
(like disallowing copying of input ranges).

It's kind of interesting how, with the ongoing discussion about 
range API in the general forum, a couple of range questions are 
brought up in learn. Something, as they say, is in the air.


More information about the Digitalmars-d-learn mailing list