Am 20.11.2015 um 16:37 schrieb Andrei Alexandrescu:
On 11/20/2015 04:42 AM, Sönke Ludwig wrote:
I think that .save is a serious design flaw in the range API

How would you redo it? -- Andrei

That's why I wrote that the alternatives had their own issues - I unfortunately don't have a solution to this either. Making usage errors fail loudly at runtime is the only one improvement I had in mind that wouldn't result in unacceptable code breakage.

Now if we could exclude reference type ranges from the picture* and ignore the fact that this would break tons of code, I think making input ranges non-copyable and using the postblit constructor to do the job of save() would be the right approach. Recently this was mentioned somewhere and the counter argument was that this wouldn't work for wrapper ranges:

    struct FilterRange(R) {
        private R _input;

        this(R input) {
            _input = input; // error: R not copyable
        }
        // ...
    }

But it does work!

    struct FilterRange(R) {
        private R _input;

        this(R input) {
            swap(_input, input); // fine!
        }
       // ...
    }

Passing a range into such a wrapper range can still be done directly in case of rvalues: FilterRange!MyRange(MyRange(...))

L-values require an explicit "move" call (which is a good thing):

    MyRange r;
    auto fr = FilterRange(r.move);

The lowering of "foreach" to "for" would also have to be adjusted accordingly.

The main drawback of using postblit for forward ranges is that it might happen that it gets invoked accidentally, resulting in hidden performance issues (IMO better than correctness issues, but anyway). That could either be mitigated by performing substantial work lazily instead of directly in this(this), or by keeping support for save() in addition to this(this), so that a valid forward range could still disable this(this) if it's costly and expose the copy functionality through save() instead.


* To still support reference type ranges, we could turn this around and define a copyref() method that creates a new range that references the same internal range object. The advantage is that this way around failure to call copyref() will result in an immediate error.

Reply via email to