How to change DList elements by means of foreach?

Brad Anderson eco at gnuk.net
Tue Aug 20 20:15:10 PDT 2013


On Monday, 10 September 2012 at 13:05:16 UTC, monarch_dodra wrote:
> On Monday, 10 September 2012 at 12:44:36 UTC, Alexandr 
> Druzhinin wrote:
>> 10.09.2012 18:37, monarch_dodra пишет:
>>>
>>> There is a know bug: foreach with ref does not currently work 
>>> these
>>> containers. The reason is that the container's front does not 
>>> actually
>>> expose a reference, but a value, and that is what is being 
>>> changed (the
>>> returned value).
>>>
>>> There is no hope in sight to really *ever* make it work, 
>>> because
>>> "container.front += 5" can't be made to work if the returned 
>>> value is
>>> not a reference: Unlike indexes that define opIndexOpAssign, 
>>> there is no
>>> opFrontOpAssign.
>>>
>>> What bothers *me* though is that the code compiles fine, 
>>> biting more
>>> than 1 user in the process.
>>>
>>> Anyways... the workaround is* making an explicit loop, with 
>>> temporary
>>> object that is fed back into front, like this:
>>>
>>> import std.container;
>>>
>>> --------
>>> void main()
>>> {
>>>    // double-linked list;
>>>    DList!int dlist;
>>>    dlist.insertFront(0);
>>>    auto slice = dlist[]; //Extract a range manually
>>>    for( ; !slice.empty ; slice.popFront() )
>>>    {
>>>      auto value = slice.front; //Extract the value
>>>      value += 50;              //Increment the value
>>>      slice.front() = value;    //Feed back into the range*
>>>    }
>>>
>>>    foreach(value; dlist) {
>>>      assert(value == 50);  //Now this works fine
>>>    }
>>> }
>>> --------
>>>
>>> Well... this *would* work, but apparently, the implementation 
>>> of
>>> DList.Range doesn't define front(T value). This makes the 
>>> Range pretty
>>> much read-only. My guess is that this was an omission on the 
>>> part of the
>>> implementer. I will fix it so that it works.
>>>
>>>
>> Good to know, but bad to do...
>>
>> If in std.container:
>> 1553:        @property T front() { return _first._payload; }
>> change to:
>> 1553:        @property *ref* T front() { return 
>> _first._payload; }
>> doesn't it solve the problem or I don't know/understand 
>> something else?
>
> Arguably yes, however, the idea is that a container is supposed 
> to have an implementation defined allocator, meaning that 
> operations "may or may mot" invalidate references. So it is not 
> allowed to return a reference.
>
> IMO, this is a valid argument for things like Array, that "can 
> and will" move objects around, without ever telling the 
> accessing ranges. Giving reference access here would be most 
> dangerous. Not impossible, but very unsafe, and Phobos strives 
> to be safe.
> The same argument applies to BinaryHeap, which is just a 
> container adaptor.
>
> However, for any "node" based structure (such as {SD}List), 
> which are structures that users usually chose *because* 
> references are *always* valid, the argument doesn't hold as 
> well. In particular, even with an implementation defined 
> allocator, there is no reason a reference can't be returned. 
> I'll try to push for reference access, but I may be turned down 
> on the simple argument of "container uniformity" :/
>
> Finally, regarding RedBlackTree, technically, you shouldn't 
> assign to a node in the tree, but rather remove re-insert, so 
> that is a non-issue.

Has anything happened since this question was asked that moves 
toward fixes this?  I just hit it myself with 
std.container.Array.  How is anyone supposed to do anything 
productive with std.container containers with this glaring 
limitation?  I'm kind of surprised std.container is even included 
with phobos with such a deficiency. Does anyone actually use 
these containers?


More information about the Digitalmars-d-learn mailing list