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