Ruling out arbitrary cost copy construction?
Steven Schveighoffer
schveiguy at yahoo.com
Thu Oct 7 06:24:41 PDT 2010
On Wed, 06 Oct 2010 19:09:15 -0400, Andrei Alexandrescu
<SeeWebsiteForEmail at erdani.org> wrote:
> On 10/6/10 12:50 CDT, Steven Schveighoffer wrote:
>> I agree all of this except for save() (not a secret to anyone who's read
>> my posts).
>
> What was your alternative design that obviates save()?
First, let me discuss why I don't like save.
Let's classify ranges into two types. Primitive ranges are ones that
provide a range interface to data that otherwise does not have a range
interface. A good example is a container range.
Compound ranges are ranges that combine or alter range functionality from
a wrapped range or set of ranges. A good example is Zip or Chain.
Let's also define the trivial save implementation as a single line: return
this;
We can assume that compound ranges don't contain a trivial solution, but
only because the range(s) they contain could implement a non-trivial
solution. So we can qualify those trivial, even though they don't have
the canonical trival solution.
Most ranges that are not compound implement the trivial solution. This
includes all dcollections ranges and *ALL* non-compound ranges in phobos.
Then there are ranges which cannot implement save whatsoever, even if they
wanted to. A range that provides the range interface for a network stream
would be a good example.
That leaves us with ranges which cannot implement the trival solution, but
can implement save. A class-based range would be a good example.
Currently, there are zero examples of this type of range. That's right,
zero (grep 'save()' std/*.d). I contend that we have no place for such
ranges in D.
So my question is, what is the point of save? The whole point is for this
last class of ranges, so they can implement a way to copy the iteration
position in a way that isn't doable via simple assignment. But there
*AREN'T ANY* of these ranges in existence. Why do we have a feature that
is littered all over phobos and any range that wants to be more than a
basic imput range when the implementation is return this; ?
Now, this isn't quite fair -- save is important not only for what it
provides but for what it excludes. It correctly excludes the ranges that
cannot implement it. And these types of ranges do exist. So if we get
rid of save, we have to compensate for this as well.
--------
So now you know why I don't like save. I'll move on to my alternative.
My solution is this. The class of ranges that cannot implement the
trivial save will define an enum refIterator to be true. And that's it.
It shouldn't hurt currently since no ranges implement a non-trivial save.
If we come across a range where it makes sense to implement save, we can
figure that out later. But our solution works easily in phobos:
size_t bringToFront(Range1, Range2)(Range1 front, Range2 back)
if (isForwardRange!Range1 && isForwardRange!Range2)
Look, there's already a template constraint for isForwardRange. Just
define isForwardRange to disqualify ranges which define the refIterator
enum, and we have to change no code (except get rid of all the save
litter).
>> I'll also note, another unsolved issue with this is iteration --
>> iterating a sealed range via foreach is not going to use moveFront.
>
> Well it shouldn't because the user doesn't want to destroy the stuff is
> iterating.
Yeah, but I also don't think by default he wants to make an arbitrary-cost
copy.
Imagine a situation like this:
foreach(i; arr)
{
if(i > 5)
return i;
}
If arr is an array of BigInt, you are copying all of them out of the
array, just to read them.
All I'm saying is moveFront simply solves the swapping problem, there are
other problems with arbitrary cost copy construction that are not solved
by moveFront.
>> Andrei wrote:
>>> 4. It would be a definite departure from C++, where all value copies
>>> are considered of arbitrary cost. This would provide a convenient
>>> straw-man for naysayers (e.g. "Hey, D calls the copy constructor even
>>> more often than C++! No thanks, I'll stick with C++0x which solves it
>>> all with rvalue references").
>>
>> Wait, shouldn't auto ref solve the rvalue references thing? I mean if
>> you are copying an rvalue, there is no reason to keep the original
>> around, so no need to call the copy constructor, right?
>
> auto ref has its uses, but designing an interface with front() but not
> moveFront() will make it impossible to move the front out of the range.
But your point was about how C++ has rvalue references, how does rvalue
references solve the moveFront problem? FWIW, I thought C++ supports
passing rvalue references only by const, no?
-Steve
More information about the Digitalmars-d
mailing list