On Wednesday, February 22, 2012 23:16:41 Blake Anderton wrote: > Why doesn't this work? I'm assuming I'm not fully understanding > how "alias this" interacts with ranges (i.e. the range isn't > being reset after count is finished with it), but I'm not sure > how to fix this either: > > import std.algorithm; > > class ArrayContainer > { > int[] values; > this(int[] v) { values = v; } > alias values this; > } > > void main(string[] args) > { > auto c = new ArrayContainer([1, 2, 3]); > assert(count(c) == 3); //succeeds > assert(c.length == 3); //FAILS - is actually zero > }
I believe that the problem stems from the fact that count is being instantiated with ArrayContainer rather than int[]. That means that when it gets processed, it ends up operating on the array directly rather than on a slice. If ArrayContainer were a struct rather than a class, then this would work. But by doing what you're doing, you've managed to create a type that effectively conflates a range and a container. So, when you pass it to range- based functions, it _will_ be consumed. You really shouldn't give direct access to the array like that. It's only going to cause you trouble. Containers should _not_ be ranges, and using alias this makes your ArrayContainer a range. Instead, you should provide an opSlice to get the array. Then you'd do assert(count(c[]) == 3); assert(c[].length == 3); The explicit [] would be required to call opSlice, since ArrayContainer would then not be passable to count (which it really shouldn't be anyway). However, if you're really married to the idea of using alias this, then the above code will work with your current implementation, since it forces the array to be sliced, and count therefore gets instantiated with int[] rather than ArrayContainer. But again, I _really_ advise against having a container which is a range. It's a _bad_ idea which is only going to cause you trouble. - Jonathan M Davis