Difference between range `save` and copy constructor

Jonathan M Davis newsgroup.d at jmdavisprog.com
Sun Feb 16 18:11:11 UTC 2020


On Sunday, February 16, 2020 10:53:36 AM MST Paul Backus via Digitalmars-d-
learn wrote:
> On Sunday, 16 February 2020 at 17:10:24 UTC, Jonathan M Davis
>
> wrote:
> > On Sunday, February 16, 2020 7:29:11 AM MST uranuz via
> >
> >> This is working fine with disabled postblit...
> >> import std;
> >>
> >> struct SS
> >> {
> >>
> >>      @disable this(this); // Disabled copy
> >>
> >>      bool _empty = false;
> >>
> >>      bool empty() @property {
> >>
> >>          return _empty;
> >>
> >>      }
> >>
> >>      void popFront() {
> >>
> >>         _empty = true;
> >>
> >>      }
> >>
> >>      int front() @property { return 10; }
> >>
> >> }
> >>
> >>
> >> void main()
> >> {
> >>
> >>      foreach( it; SS() ) { writeln(it); }
> >>
> >> }
> >>
> >> Am I missing something?
> >
> > That code compiles, because you're passing a temporary to
> > foreach. So, the compiler does a move instead of a copy. It's
> > the difference between
> >
> > auto ss = SS();
> >
> > and
> >
> > SS ss;
> > auto ss2 = ss;
> >
> > If your main were
> >
> >     void main()
> >     {
> >
> >         SS ss;
> >         foreach( it; ss ) { writeln(it); }
> >
> >     }
> >
> > then it would not compile.
>
> On the other hand, this does work:
>
>      void main()
>      {
>          SS ss;
>          foreach( it; move(ss) ) { writeln(it); }
>      }
>
> So perhaps the correct approach is to use `move` when copying
> input ranges.

Given that the way that almost all range-based functions work is to copy the
range that they're given (often then wrapping it in another range that's
returned), I don't see how it would make sense to use move outside of very
specific circumstances. If you pass a range to a function, and it's a basic
input range, then you just use the range via the return value (be it the
same range returned directly or returned within a wraper range), and if it's
a forward range, you call save before passing it to the function if you want
to be able to use the range directly again. Either way, generic code should
never be using a range after it's been copied, and copying is a key part of
how idiomatic, range-based code works in D.

And really, using move just to be able to use an uncopyable range with
foreach doesn't make a lot of sense, since if that's what you want to do,
you can always just use a normal for loop. Regardless, there isn't much
point in declaring a range type that can't be copied, since it's pretty much
only going to work with code that you write.

- Jonathan M Davis





More information about the Digitalmars-d-learn mailing list