readonly?
Jonathan M Davis
jmdavisProg at gmx.com
Wed Jul 11 16:09:38 PDT 2012
On Wednesday, July 11, 2012 10:56:23 Artur Skawina wrote:
> Can anybody think of a reason to keep the current (broken) behavior?
Easily.
Making Object* point to the object itself rather than the reference would be
so broken that it's not even funny. I can understand why you would think that
such a change should be made, but that's only because you haven't thought
through all of the consequences of that. It's all wrapped up in the very
reason why Rebindable is in the library rather than being built into the
language.
For starters, if you have
class C {}
C c;
the type of c is C. C is a reference to the class named C. It does _not_ mean
the same thing as the C in the class declaration at all. If it were a struct
rather than a class, it would be equivalent to C* (save for the fact that a
reference and a pointer aren't quite the same). So, _everywhere_ in the type
system that you see C, it's really C*, _not_ C as in the class C. There is _no
way_ in the type system to refer to the class itself. So, making C* be a
pointer to the object rather than the reference isn't even representable in
the type system. It's exactly the same in Java and C# with their classes.
Walter tried to get around this to implement Rebindable in the language and
gave up on it. It may be possible, but it's _ugly_ if it is.
There are all kinds of practical side effects for this. For instance, if you
had
class D : C {}
C c;
C* cPtr = &c;
and C* was a pointer to the class itself, then it would almost be like doing
C* c;
C* cPtr = &c;
which makes no sense at all. It also introduces object slicing - as in the
type of slicing that C++ has when you assign a derived object to a base class
object which is on the stack. In C++,
D d;
C c = d;
results in the D portion of d being chopped off, potentially putting it in an
invalid state, and almost certainly isn't what you wanted to do. D avoids this
by requiring that polymorphic objects (classes) be on the heap. But if C*
referred to the object itself, it becomes a probem again.
C c = new C;
C* cPtr = &c;
*cPtr = new D;
Since cPtr was a pointer to the object, *cPtr would be the object itself, and
so the D object would be assigned to the C object and get sliced, just like in
the C++ example.
In additon, making Object* be a pointer to the object itself would make
dealing with pointers to local objects _very_ inconsistent. Take this for
example
void func(T)(T* t, T value)
{
*t = value;
}
int i;
func(&i, 7);
C c = getC();
func(&c, new C);
When func is called with &i, it sets i to 7. But when it's called with &c, it
doesn't set c. It sets what c refers to. This means that instead of changing
the local variable c, you've changed an object on the heap which other
references could refer to, and now instead of just affecting the local
reference, _every_ reference is affected. This is _completely_ different from
how it works with &i. It's more in like with how it would work if you had
int* iPtr = getIntPtr();
func(&iPtr, new int);
and since C is essentially equivalent to C*, it would then be impossible to
pass the reference itself to func to be set.
It would also make it so that taking a pointer for a parameter was very
different from taking ref or out for classes, when it's nearly identical for
everything else.
void refFunc(T)(ref T t, T value)
{
t = value;
}
int i;
refFunc(i, 7);
C c = getC();
refFunc(c, new C);
With your suggestion, this code operates identically for i but does something
completely different for c. Now, instead of setting the object, it's setting
the reference.
AAs would also be very broken if Object* pointed to the object rather than the
reference. Take this code:
int[string] aa;
int* intPtr = "hello" in aa;
C[string] bb;
C* cPtr = "hello" in bb;
With aa, you get a pointer to the value which is at the key "hello". With bb,
you get a pointer to the object which the value at "hello" refers to. So, once
again, setting *cPtr sets the object rather than the reference, and slicing
becomes a problem. On top of that, what happens with null? Right now, you can
do
bb["hello"] = null;
C* v1 = "hello" in bb;
C* v2 = "world" in bb;
The fact that v1 is non-null tells you that "hello" is in bb, and the fact
that v2 is null tells you that "world" isn't in bb. You can then dereference
v1 and get at the value which is at "hello", which is null. But what happens
when C* nows points to the object rather than the reference? It becomes
impossible to distinguish between the case when the key isn't in the AA and
when the value at that key is null. You could fix that by making it so that
C[string] bb;
was implicitly
C*[string] bb;
but then the type that in returns would have to be C**, making it so that
Objects behaved differently with AAs than every other type, since in all other
cases with T[U], in returns T*, not T**.
I could go on, but this post is already ridiculously long. The point is that
the type system is built around the fact that class variables are _always_
references and that there is no way to refer to such an object directly. If
you start referring to the object directly, things break. The type system just
does not support that, and making &c give you pointer to the object rather
than the reference would make it _completely_ inconsistent with the rest of
the language, much as it might seem otherwise at first. You just have to
realize that whenever you see Object (or any other class type) used, it's a
reference, _not_ the type of the object itself, and that it's impossible
(within the type system) to refer to the object itself, so the behavior of &c
is _completely_ consistent with the rest of the language.
- Jonathan M Davis
More information about the Digitalmars-d-learn
mailing list