The tailconst problem (and suggestions for solution)

Janice Caron caron800 at googlemail.com
Fri Dec 7 01:40:36 PST 2007


Walter, I have thought very hard about what you said, and I have come
to the conclusion that Andrei is absolutely correct. However, there
might be some things you can do to mitigate things a little, to make
things slightly easier for the programmer.

Once I realised that my ampersand idea wouldn't work, because it would
set up a contradiction between the requirements of generic
programming, and the requirement of being able to wrap anything in a
struct, my next thought was a new keyword: "tailconst". (Actually, two
new keywords, because you'd also need "tailinvariant"). With this
keyword,

    class C {}
    tailconst(C) c;

would mean "c is a mutable ref to const data". Of course that does
then beg the question, what does tailconst(T) mean for T in general,
if T is not a class. The answer is:

(1) from the perspective of a human being writing code, consider it
undefined. Just don't use it. Except with classes, there is never a
need to use it all, as everything you want can be expressed more
clearly with const, and

(2) from the perpective of the compiler, one can come up with some
rules to make it all work properly under the hood. I did actually come
up with most of what those rules would need to be (...and I can
certainly tell you if you want...), but then it occurred to me that
that must be exactly what Andrei did when he proposed his template
idea.

In other words, I suspect that my "tailconst(C)" idea is identical in
practice to Andrei's "TailConst!(T)" idea, the only difference being
that I made up new keywords and Andrei didn't. I still think that the
tailconst keyword idea is /slightly/ preferable to the TailConst!
template idea, if only because of the implementation of the
specialization for classes. "tailconst" could give us compiler support
for the notion of "mutable ref to const data", whereas TailConst!
might involve a lot of fiddling about.

The third possibility is of course, doing without it altogether.

That's easy to do in principle: If you want a mutable reference, use a pointer!

    class C {}
    const(C)* c;

...or arrays

    class C {}
    const(C)*[] a;

There are two problems with this, however. Neither of the following
two lines will compile:

    a[3] = new C;
    auto x = a[3],member;

The first one won't compile, because the compiler won't implicitly
convert a reference-to-class-data into a
pointer-to-reference-to-class-data (and with good reason!). The second
one won't compile because the type of a[3] is
pointer-to-reference-to-class-data, not reference-to-class-data, and
the dot operator will only dereference once.

The only way I can think of getting around this is to allow two new
rules (one of which I've actually asked for before). The first is:

(1) Make it possible to implicitly convert a reference-to-class-data
into a pointer-to-reference-to-class-data. The implementation of this
trick must first copy the reference onto the heap, so that the pointer
points into the heap, not into the stack.

(2) (and this is the one I've asked for before) allow dereferencing to
be recursive, so that p.member will work even if p is a
pointer-to-pointer-to-pointer-to-pointer-to-data.

I think those two rules will let us work with const(C)*[], and hence
do away with the need for tailconst altogether.



More information about the Digitalmars-d mailing list