Const template

Andrei Alexandrescu (See Website For Email) SeeWebsiteForEmail at erdani.org
Sun Jan 21 19:19:50 PST 2007


Frits van Bommel wrote:
> You didn't respond to my paragraph on classes though:
> 
> Frits van Bommel wrote:
>  > I also don't see a way this can work for classes (in general). There's
>  > no way to limit a non-final method to non-mutating operations that I'm
>  > aware of, so it could easily be circumvented by subclassing. Even though
>  > that would only allow direct access to non-private fields, mutating base
>  > class methods could still be called for the rest (if available). So then
>  > the only way to get a const version of a class would be if the class
>  > doesn't allow mutations anyway (like java's String). Which leaves us
>  > right back where we started :(.
>  >
>  > And I don't think a 'const' implementation should work for structs and
>  > not for classes...

I did when I wrote:

 >> That is correct. Interface functions, nonfinal methods, and
 >> declared-only functions must be annotated manually.

I meant - yes, you are right.

Today, Walter and myself have come with a semantics for const (as in, 
"read-only view" that) seems to be a good starting point. The details 
are yet to be ironed out, but here's the basic plot:

* const is a unary type constructor (takes a type, returns a type)

* const is always transitive, meaning that once a value is const, the 
entire part of the world accessible through that value is const as well. 
This distances D from C++'s shallow view of const (only one level).

* const is not removable legally via a cast. But the compiler must 
assume in the general case that a const view may alias with a modifiable 
view. This is a weak point of the design. The hope is that in most cases 
the compiler will easily prove non-aliasing and can do a good job at 
optimizing based on const. Example: all pure functions don't care about 
aliasing to start with.

* Shallow const is achievable via final. Final only means a value is not 
supposed to be changed, but anything reachable through it can be mutated.

* The syntax for manually-annotated const is:

struct Widget
{
   void Foo(const)(int i) { ... }
}

This is in the spirit of current D, because Foo can be seen as a 
specialization for a subset of Widget types.

* If you do want to make Foo a template, it's:

struct Widget
{
   void Foo(const, T)(T i) { ... }
}

* D avoids the issue of duplicated function bodies in the following way:

struct Widget
{
   storageof(this) int* Foo(storageof(this))(int i) { ... }
}

This code transports whatever storage 'this' has to the result type. 
More involved type computations are allowed because inside Foo, 
typeof(this) includes the storage class. Effectively Foo above is a 
template.

* Constructors and destructors can figure the storage class of the 
object being constructed. This is useful for selecting different 
allocation strategies for immutable vs. mutable objects:

class Widget
{
   this(int i) { ... }
   this(const)(int i) { ... }
   ~this() { ... }
   ~this(const)() { ... }
}

Const cdtors can obviously mutate the object being cdted. In a cdtor, 
'const' is not enforced -- it's just an information for the programmer.

* Walter sustains that given the above, there will never be a need to 
overload member functions based on const. I disagree, but I couldn't 
come up with a salient example. Besides, I don't care that much because 
the semantics above do allow telling const from nonconst, just in a 
roundabout way.


Andrei



More information about the Digitalmars-d mailing list