Why in Phobos is empty() sometimes const and sometimes not
Jonathan M Davis
newsgroup.d at jmdavisprog.com
Tue Jul 30 05:30:30 UTC 2019
On Monday, July 29, 2019 3:11:45 PM MDT Matt via Digitalmars-d-learn wrote:
> On Monday, 29 July 2019 at 19:38:34 UTC, Jonathan M Davis wrote:
> > On Monday, July 29, 2019 11:32:58 AM MDT Matt via
> > Digitalmars-d-learn wrote:
> >
> >
> >
> > Because const ranges are basically useless, there really isn't
> > much point in putting const on any range functions even if it
> > would work for that particular range, and if a range is a
> > wrapper range, the only way that it could do it would be if it
> > used static if to make the code differ depending on whether the
> > range it's wrapping will work if that function is const, which
> > essentially means duplicating a bunch of code for little to no
> > benefit.
> >
> > - Jonathan M Davis
>
> This was super helpful, thanks so much. I thought it was good
> practice to label member functions const if they didn't/couldn't
> modify any data. Now I see the reality is different for ranges.
> Not worrying about const for these seems simpler than code
> repetition.
>
> Thanks again.
In principle, it's good to use const when you know that data isn't going to
change, but that gets far more complicated when you're dealing with generic
code or even with classes, since as soon as you use const, everything used
with that template then needs to work with const, or in the case of classes,
every derived class has to use const for their override of that function.
Sometimes, that's fine, but that's usually when you're either requiring that
const always work, or you're in control all of the code involved, so you
know that you're not going to have to deal with issues like caching. It's
issues like this that led us to decide a while ago that putting functions on
Object was a mistake, since it locked all classes into a particular set of
attributes (and even if we changed which attributes those were, it would
still cause problems). The ProtoObject DIP (which would add a base class for
Object that didn't have anything on it) will hopefully fix that, but that
still hasn't been finalized yet.
In the case of ranges, on top of the general issues with const and generic
code, their API just isn't designed with const in mind. Fundamentally, you
need to be able to mutate a range to iterate through it. It would be
different if we'd gone with more of a functional-style, head/tail solution
where you have a function like head to get the first element, and a function
like tail to return a range with the first element popped off, but for
better or worse, that's not the direction we went. However, even if we had,
issues like caching or delayed calculation would still come into play, and
if you require that const work on something like empty, that prevents
certain classes of solutions. Of course, on the flip side, without const,
you don't know for sure that unwanted mutation isn't happening, but what
would really be needed would be some kind of "logical" const rather than the
full-on const we currently have, and that would be very difficult to
implement. C++'s const _sort_ of allows that, because it has so many
loopholes, but on the flip side, you lose most of the guarantees, and it
mostly just becomes documentation of intent rather than actually enforcing
logical constness. In practice, I find that D's const tends to not be
terribly useful in generic code, but it's far more of a problem with
libraries that are publicly available than with code where you control
everything and can change stuff when you need to. This article I wrote goes
into further detail about the issues with const in general:
http://jmdavisprog.com/articles/why-const-sucks.html
The situation with ranges would be improved if we had some kind of const or
inout inference for templated code like we do with other attributes, but I
don't know quite how that would work or what the downsides would be.
- Jonathan M Davis
More information about the Digitalmars-d-learn
mailing list