Ruling out arbitrary cost copy construction?

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Wed Oct 6 16:09:15 PDT 2010


On 10/6/10 12:50 CDT, Steven Schveighoffer wrote:
> On Wed, 06 Oct 2010 12:34:54 -0400, Andrei Alexandrescu
> <SeeWebsiteForEmail at erdani.org> wrote:
>
>> I think ranges are a great thing, having simplicity as one of their
>> advantages. In the beginning they were indeed rather simple:
>>
>> struct MyRange {
>> bool empty();
>> ref Widget front();
>> void popFront();
>> }
>>
>> with the appropriate refinements for bidirectional and random.
>>
>> Then there was the need to distinguish one-pass, "input" ranges (e.g.
>> files) from many-pass, "forward" ranges. So the "save" function got
>> born for forward ranges and above:
>>
>> struct MyRange {
>> bool empty();
>> ref Widget front();
>> void popFront();
>> MyRange save();
>> }
>>
>> Then @property came about which required extra adornments:
>>
>> struct MyRange {
>> @property bool empty();
>> @property ref Widget front();
>> void popFront();
>> @property MyRange save();
>> }
>
> or more succinctly:
>
> struct MyRange {
> @property {
> bool empty();
> ref Widget front();
> MyRange save();
> }
> void popFront();
> }
>
>>
>> Then some ranges were unable to return ref, but they could receive
>> assignment. I call these /sealed ranges/ because they "seal" the
>> address of their elements making it inaccessible:
>>
>> struct MyRange {
>> @property bool empty();
>> @property Widget front();
>> @property void front(Widget);
>> void popFront();
>> @property MyRange save();
>> }
>>
>> This made bidirectional and random-access range interfaces quite big
>> because now we're talking about back() (two versions), opIndex(),
>> opIndexAssign() and opIndexOpAssign().
>>
>> Until now I think we're solid on design decisions. The discussion
>> starts here.
>
> 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()?

>> To solve that problem, I introduced moveFront(), moveBack(), and
>> moveAt(size_t), all of which destructively read the front, back, or an
>> indexed element respectively off the range. Then you can swap r1.front
>> with r2.front at constant cost like this:
>>
>> T tmp = r1.moveFront();
>> r1.front = r2.moveFront();
>> r2.front = move(tmp);
>
> 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.

>> 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.


Andrei



More information about the Digitalmars-d mailing list