Const sucks

Regan Heath regan at netmail.co.nz
Tue Sep 11 02:42:24 PDT 2007


Daniel Keep wrote:
  > This problem seems to be unique to structs and classes.  This is because
> every other reference type is written in such a way that you can "split"
> the type in such a way as to get tail-const semantics.  For example:
> const(char[]) versus const(char)[].  This can't be done with structs or
> classes since their types are single identifiers.

I came to the same conclusion, the basic problem is the inability to put 
a * or [] outside the () of const.

> So what we need is some way to write that split.  Really, what we're
> saying is "I don't want *this* to be const, but I do want what it
> references to be const".

Exactly.

> So, stealing a little bit of C++ syntax, what about "const& T" being a
> tail-const version of some struct or class T?  Since this could
> potentially introduce synonyms in other places, I would also propose the
> following:
> 
>   const& int a; // Error: cannot use reference const on an atomic type
>   const& int* b; // Error: equivalent to const(int)*
>   struct V { int a; }
>   const& V c; // Warning: V does not have any references
>   struct R { int* a; }
>   const& R d; // OK
>   class C {}
>   const& C e; // OK
> 
> Yes, this means there is a distinction between structs, classes and
> "everything else".  But honestly, I don't think we can have tail const
> without this distinction.  Maybe disallowing const& on arrays and
> pointers isn't a good idea; but I do think we need some kind of
> tail-const for structs and classes.
> 
> Thoughts?

In D when we write:

Foo f;

we are declaring a reference to a Foo.  The problem, as you mentioned is 
the inability to seperate the reference from the Foo for purposes of 
declaring const, so...

I was initially thinking that for classes we might write:

class Foo { int a; }
const(Foo)& pFoo;  //pFoo can change, pFoo.a cannot

Where the & doesn't introduce another reference or level of indirection, 
in fact it does _nothing_ at all except allowing us to place it outside 
the () of const.

Meaning, that these two declarations would in fact be identical:

Foo& pFoo;
Foo  pFoo;

which will no doubt bother some people, perhaps a lot of people?

The first form could be made illegal, after all it's pointless(right?) 
to take a reference to a reference.

Alternately, and I think I might prefer this solution, perhaps _adding_ 
something to the const() is the solution. eg.

class Foo { int a; }
const(*Foo) pFoo;  //pFoo can change, pFoo.a cannot

As in, were saying "the value of Foo is const", therefore implying the 
reference is not.


As for structs, as someone else has mentioned there isn't really any 
difference between making a struct variable const and making all members 
of that struct const, without a pointer or reference they are the same 
thing.  So, saying:

struct Bar { int a; Foo pFoo; }
const(Bar) bar;

Is perfectly sufficient in that case.

The difference occurs when you want to make _some_ of the members consts 
and not the others, typically you want to make references/pointers 
const, or tail const.

The syntax:

struct Bar { int a; Foo pFoo; }
const(Bar)& bar;

doesn't make the same sense for structs because there is no reference 
involved (as many programmers would expect upon reading that).

Neither does the * syntax I proposed above:

struct Bar { int a; Foo pFoo; }
const(*Bar) bar;

because that would logically make all members of the struct const, and 
we get that already.

Assuming there is even a requirement to make select members of a struct 
(initially declared without const) const (if there isn't we have no 
problem) then using a template seems the most sensible solution.  It 
would also allow you to apply const or tail const where appropriate. eg.

 From this initial struct:

struct Bar {
   int a;     //we want this to be const
   Foo pFoo;  //we want this to be tail const
}

Our template generates this:

struct BarConst
{
   const int a;
   const(*Foo) pFoo;
}

Regan



More information about the Digitalmars-d mailing list