The problem with const (and a solution)

Steven Schveighoffer schveiguy at yahoo.com
Fri Dec 7 08:18:46 PST 2007


Const sucks again.  The underlying problem with const in D is that classes 
cannot be value types.  Since a class cannot be a value type, there is no 
need to put a pointer indicator in variable declarations.  Declaring a class 
type is the same as a class pointer. Throughout this example, I'll define 
two types:

class C {}
struct S {}

OK, so why is this such a problem?  Because it is a fairly common 
requirement to have a mutable pointer to const data.  We call this, 
tail-const.  How does one distinguish a mutable pointer to const data? 
Walter came up with the good idea of using const as a function to specify 
which part of a type declaration is const.  For structs, this is great:

const(S)* s;

Now, s is not const, but what s points to IS.  I really like this idea. 
However, classes DO NOT separate the pointer from the type.  i.e., when I 
declare a class type, it is already a pointer:

const(C) c; // can't get at the pointer part, so c is not mutable.

because the pointer is built into the type declaration, there is no way to 
take it outside the parentheses.  In a valiant effort to allow for this type 
of thing, Walter proclaimed that const(C) means that C is 'tail-const', 
meaning that the value type that this declared was mutable, but anything it 
pointed to was const.  This caused much confusion and was the cause of 
thread war I, 'const sucks' (previously known as the great thread war, 
matched only by thread war II, 'Phango').  But how do we pull out that 
pointer?  Well, if C is a pointer, then dereference the pointer.  i.e.:

const(*C)* c;

This means, the value part of C is const, but the pointer part is not.  Yes, 
it is ugly, but it is clear what the meaning is.  Note that *C should only 
be usable if you want to split out the pointer, you couldn't do:

*C c;

However, the problem still exists in generic code:

f(T)(const(T)* t)
{
}

This means something completely different depending on if t is a struct or t 
is a class.  This problem is NOT FIXABLE with the current proposed regime by 
Walter.  But with my syntax, this can be worked around in the following way:

template(T) vtype
{
  static if(is(T == class))
     alias *T vtype;
  else
     alias T vtype;
}

f(T)(const(vtype!(T))* t)
{
}

So this is even uglier.  However, I think there may be a solution that is 
not ugly, but still gives us the same usability.  If the & operator is used 
to mean 'pointer to the value type', this could be used to mean the SAME 
thing for both classes and structs.  For example:

C& c; // reference to a C class
S& s; // reference to a S struct

anything that has & in it levels the playing field.  Note that for classes, 
C& c is equivalent to C c, and S& s is equivalent to S* s.  What does this 
give us?  Now we can split out the pointer for tail-const anything:

const(T)& t;

This means a mutable pointer to a const T, where T is either a value type 
(struct, int, etc) or a class value (not a reference to a class, but the 
class data itself).  This gives us a tail-const T no matter if T is a value 
type or a class type.

This solution is by far my favorite, but I can certainly live with the *C 
notation.  However, I think tail-const for classes NEEDS TO EXIST.  Code 
that correctly uses const will be ugly and cumbersome without it, and what 
will end up happening is people will just not use const at all except for 
strings.

-Steve 





More information about the Digitalmars-d mailing list