User defined type and foreach

Tony tonytdominguez at aol.com
Sat Nov 18 05:24:30 UTC 2017


On Friday, 17 November 2017 at 17:55:30 UTC, Jonathan M Davis 
wrote:
>
> When you have
>
> foreach(e; range)
>
> it gets lowered to something like
>
> for(auto r = range; !r.empty; r.popFront())
> {
>     auto e = r.front;
> }
>
> So, the range is copied when you use it in a foreach. In the 
> case of a class, it's just the reference that's copied. So, 
> both "r" and "range" refer to the same object, but with a 
> struct, you get two separate copies. So, when foreach iterates 
> over "r", "range" isn't mutated.

Ah, I get it now ("r=range; process r"), thanks!

>
> So, in the general case, if you want to use a range in foreach 
> without consuming the range, it needs to be a forward range, 
> and you need to call save. e.g.
>
> foreach(e; range.save)
>

Seems like you can make class-based ranges to work on multiple 
foreach calls without having to do save, although maybe it falls 
apart in other usage. It also doesn't appear that the compiler 
requires an @property annotation as specified in the interface :

import std.stdio : writeln;

class RefRange {
     int foreach_index;
     int[] items;
     this(int[] src)
     {
        items = src;
     }

     bool empty()
     {
        if (foreach_index == items.length)
        {
	  foreach_index = 0; // reset for another foreach
	  return true;
        }
        return false;
     }
     int front() { return items[foreach_index]; }
     void popFront() { foreach_index++; }
}

void main() {
     import std.stdio;

     int[] ints = [1, 2, 3];
     auto refRange = new RefRange(ints);

     writeln("Ref 1st Run:");
     foreach(i; refRange) writeln(i);
     assert( ! refRange.empty);
     writeln("Ref 2nd Run:");
     foreach(i; refRange) writeln(i); // works
}
------------------------------------------
Ref 1st Run:
1
2
3
Ref 2nd Run:
1
2
3


More information about the Digitalmars-d-learn mailing list