Difference between range `save` and copy constructor

Jonathan M Davis newsgroup.d at jmdavisprog.com
Sun Feb 16 17:39:38 UTC 2020


On Sunday, February 16, 2020 6:52:17 AM MST uranuz via Digitalmars-d-learn 
wrote:
> It's very bad. Because there seem that when I use range based
> algorithm I need to take two things into account. The first is
> how algrorithm is implemented. If it creates copies of range
> inside or pass it by reference. And the second is how the range
> is implemented if it has value or reference semantics. So every
> time I need to look into implementation and I can't rely on API
> description in most of the cases. In a lot of cases Phobos uses
> value semantics. But there are cases where I want the range
> actually be consumed, but it's not. And the other problemme is
> when algorithm expects range to have value semantics, but it's
> not. So it's a buggy mess that it's hard to think about. In
> trivial cases this is working although. But in more complex cases
> it's simplier to implement some algorithms by own hands so that
> it would work as I expect it myself rather that thinking about
> all these value-ref-range mess. But still can't say that I
> implement it correctly, because range specification actually
> sucks as yo say.
> It's just horrible

The current situation is definitely not ideal, but it can be used quite
consistently. Just follow the rule that once you copy a range, you do not
use that range ever again unless you assign it a new value. So, if you do
something like

auto result = r.find(e);

then you should not be using r again unless you assign it a new value.
e.g.

r = r.find(e);
r.popFront();

auto result = r.find(e);
r = otherRange;
r.popFront();

would be fine, because r was assigned a new value, whereas

auto result = r.find(e);
r.popFront();

should never happen in your code unless you know that typeof(r) is a type
where copying it is equivalent to calling save on it. In generic code, that
means that you should never use a range again after passing it to a function
unless you assign it a new value, or that function accepted the argument by
ref (which almost no range-based functions do).

If you want to be able to use the range again after passing it to a function
without assigning it a new value, then you need to call save so that you
pass an independent copy. e.g.

auto result = r.find.save(e);
r.popFront();

The only copying going on here is with save, so there's no problem, whereas
if r were passed to find directly, the behavior is implementation-dependent
- hence why you should not be using a range after it's been copied (which
includes passing it to a function).

The only time that generic code can reuse a range after passing it to a
function is if that function accepts its argument by ref, which almost no
range-based code does. Far more frequently, it returns the result as a
wrapper range, in which case, you can't use the original range any longer
unless you used save when calling the function or if the code is not generic
and you're coding based on the specific behavior of that particular range
type - which usually isn't something that code should be doing.

By no means do I claim that the status quo here is desirable, but if you
just follow the simple rule that you don't ever use a range once it's been
copied (unless that copy came from save), then you shouldn't run into
problems related to the fact that different ranges have different copying
semantics unless the function that you're calling is buggy.

If you're going to run into a bug along those lines though, it's likely
going to be because a function didn't call save when it was supposed to, and
it was only tested with range types where copying them is equivalent to
save. That's why it's important to test range-based code with both range
types where copying them is equivalent to save and range types which are
full-on reference types (and thus copying just results in another
reference). In general though, any range that is a forward range should have
copying it be equivalent to save, and using reference types for forward
ranges tends to be inefficient and error-prone even if range-based functions
(especially those in Phobos) should be able to handle them correctly.

- Jonathan M Davis





More information about the Digitalmars-d-learn mailing list