Topic: Mutable references?


Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Mon, 27 Jun 1994 15:34:32 GMT
Raw View
In article <JASON.94Jun24131109@deneb.cygnus.com> jason@cygnus.com (Jason Merrill) writes:
>>>>>> Steve Clamage <clamage@taumet.Eng.Sun.COM> writes:
>
>> The result of casting a struct* to an int* is undefined. Section 4.6
>> [conv.ptr] of the WP lists the allowed pointer conversions, and this
>> isn't one of them.
>
>[conv.ptr] lists the pointer conversions which will be applied implicitly,
>but these are not the only allowed pointer conversions.  [expr.static.cast]
>and [expr.reinterpret.cast] talk about pointer conversions which can be
>applied explicitly; no pointer conversions are undefined.  Some may be
>implementation-defined, but none will make prank calls to the White House.

 Whether or not thats so at the moment, its likely
to change. There's a good chance almost all reinterpret casts
will be undefined.

 struct A* --> struct B*

will be well defined.  The rule for pointer reinterpret_cast
will be that it is well defined only if the source and destination
have the same object type. (Note: I mean the POINTERS)

 int and unisgned have the same object type.
I'm told int* and unsigned* do not. So a reinterpret cast
between them will be undefined.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: jason@cygnus.com (Jason Merrill)
Date: Tue, 28 Jun 1994 04:12:15 GMT
Raw View
>>>>> John Max Skaller <maxtal@physics.su.OZ.AU> writes:

>  int and unisgned have the same object type.  I'm told int* and
> unsigned* do not. So a reinterpret cast between them will be undefined.

Are you suggesting, then, that

main()
{
  int i = 1;
  unsigned *p = reinterpret_cast<unsigned*>(i);
  int *q = reinterpret_cast<int*>(p);

  cout << *q;
}

Will produce undefined behavior?  Let's try to separate the semantics of
initializing p and the semantics of trying to "use" *p.  I would argue that
the one is well-defined and the other is either unspecified or undefined,
depending on how you feel about interpreting bit patterns.  I thought you
were on the unspecified side of that particular fence...

Jason




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 29 Jun 1994 02:26:37 GMT
Raw View
In article <JASON.94Jun27211215@deneb.cygnus.com> jason@cygnus.com (Jason Merrill) writes:
>>>>>> John Max Skaller <maxtal@physics.su.OZ.AU> writes:
>
>>  int and unisgned have the same object type.  I'm told int* and
>> unsigned* do not. So a reinterpret cast between them will be undefined.
>
>Are you suggesting, then, that
>
>main()
>{
>  int i = 1;
>  unsigned *p = reinterpret_cast<unsigned*>(i); //#1

 I presume you mean &i?

>  int *q = reinterpret_cast<int*>(p); //#2
>
>  cout << *q;
>}
>
>Will produce undefined behavior?

 Yes. Note I dont AGREE with that but its the rule
Bill Plauger says is correct for ISO C. I prefer a rule:

 "if T1 and T2 have the same object type, then
 so too do pointers to those types"

which rule is recusive on pointers and appeals to my sense
of mathematical symmetry. The reasoning for int* and unsigned*
to have different object types eludes me, but suppose
its the case. Suppose that we consider char* and int*
on a machine for which 16 bit even addresses denote
16 bit words.  A char* is implemented as a 32 bit pointer,
with the 'hi' or 'lo' byte of the word signified in the high
word of the address.

 int i;
 int *ip = &i;
 char * cp = reinterpret_cast<char*> (ip);
 int * ip2 = reinterpret_cast<int*> (cp);

Its clear that this cast cannot be done -- in EITHER direction.
This is not because the types int and char do not have the
same represntation:

 class A { float x; };
 class B { int x; };
 A i;
 A *ip = &i;
 B * cp = reinterpret_cast<B*> (ip);
 A* ip2 = reinterpret_cast<A*> (cp);

This cast is legal. All pointers to structs have the same object
type. Naturally incorrect USE of the pointers will be a violation,
but the cast just involves copying the bit patterns of
the pointers.

That is NOT the case for char* and int*: that would result
in invalid pointers (by which I do NOT mean that they
fail to point to the an appropriate object, I mean
the pointer has an illegal representation and is not
interpretable as an address).

Bill says the same applies to int* and unsigned* -- I cant
understand exactly why, or how two pointers to the SAME
object type could have distinct representations, but that
doesnt mean I'm right and Bill and/or ISO C is wrong and should
be changed.

<Whew, sorry, I found that difficult: in particular,
I discovered that reinterpret cast is trickier than I thought>

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: clamage@taumet.Eng.Sun.COM (Steve Clamage)
Date: 21 Jun 1994 21:43:07 GMT
Raw View
In article 46H@microsoft.com, jimad@microsoft.com (Jim Adcock) writes:
>In article <pkt-160694102318@138.52.2.129> pkt@lpi.liant.com (Scott Turner) writes:
>|"This notion" that a pointer points to an object of the dereferenced
>|type of the pointer is plainly basic.
>
>Whether the notion is basic or not, it is clearly contradicted by
>the design of the language,

If so, the example given does not illustrate the point.

>                             and thus one must discard this 'basic'
>notion for one more consistent with the actual design of the language.
>
>Consider, trivially:
>
> FOO* foo = new FOO;  // I'm a foo!
>
> int* i = (int*)foo;
>
> *i = 1234;  // No, I'm an int!;
>
> foo.DoFooStuff(); // CRASH!!!

The result of casting a struct* to an int* is undefined. Section 4.6
[conv.ptr] of the WP lists the allowed pointer conversions, and this
isn't one of them. Once you use an undefined operation, you can draw
no conclusions. (I'm assuming FOO is a struct type. If not, the objection
still stands, unless FOO is an int, or a union one of whose members is
an int, in which case the code is OK.)

We can perform certain pointer conversions with well-defined results,
and in all of those, Scott's contention holds.

---
Steve Clamage, stephen.clamage@eng.sun.com





Author: jason@cygnus.com (Jason Merrill)
Date: Fri, 24 Jun 1994 20:11:09 GMT
Raw View
>>>>> Steve Clamage <clamage@taumet.Eng.Sun.COM> writes:

> The result of casting a struct* to an int* is undefined. Section 4.6
> [conv.ptr] of the WP lists the allowed pointer conversions, and this
> isn't one of them.

[conv.ptr] lists the pointer conversions which will be applied implicitly,
but these are not the only allowed pointer conversions.  [expr.static.cast]
and [expr.reinterpret.cast] talk about pointer conversions which can be
applied explicitly; no pointer conversions are undefined.  Some may be
implementation-defined, but none will make prank calls to the White House.

Jason




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Mon, 20 Jun 1994 07:21:36 GMT
Raw View
In article <9417101.14261@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>rfg@netcom.com (Ronald F. Guilmette) writes:
>
>>fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>>rfg@netcom.com (Ronald F. Guilmette) writes:
>>>
>>>>As far as I know, there are no such things as ``constraints'', either in the
>>>>ARM or in the most recent ANSI/ISO C++ standardization draft.
>[...]
>>An international language standard should not be reliant upon unstated
>>definitions and/or informal understandings.
>
>This is true.  It is however irrelevant to the earlier topic of this
>thread, which was asking about the legality of a particular piece of
>code.

You are right (of course) and I was just shamelessly using the earlier
discussion as an excuse to talk about what I see as one of the bigger
remaining problem areas in the current draft of the C++ working paper,
i.e. the fact that all of the necessary distictions between usage which
must trigger diagnostics (from a conformant compiler) and usage which
merely results in undefined behavior have not yet been made (in the
working paper).

(This is one area where I think I may be able to help the committee,
e.g. by identifying all such remaining cases.)

>>This is a rather important distinction.  There is illegal and then there
>>is ILLEGAL.
>
>Sure.  I thought it would have been fairly clear from the context that
>I meant `illegal' rather than `ILLEGAL' ;-)

To paraphrase Will Rodgers `I only know what I read in the (working) papers.'
Thus, there are lots of examples of small hunks of invalid code which I
(for one) am still unable to categorize as either `illegal or as `ILLEGAL'.

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- domain addr: rfg@netcom.com ----------- Purveyors of Compiler Test ----
---- uucp addr: ...!uunet!netcom!rfg ------- Suites and Bullet-Proof Shoes -




Author: jimad@microsoft.com (Jim Adcock)
Date: Mon, 20 Jun 1994 18:55:49 GMT
Raw View
In article <2toagu$4kf@f111.iassf.easams.com.au> rjl@f111.iassf.easams.com.au (Rohan LENARD) writes:
|Why not.  It is quite valid (though patently not very useful if the derived
|object is used for this purpose only), to treat the base class subobject as
|an object of the base class.
|Sure the complete object isn't a base class type object.
|However (in the absence of virtual functions) the extra functionality
|provided by the specialisation is just 'padding' within the context of
|its use as a base class object.
|It doesn't matter that the derived object is a kind of object.

This oft-held position is easy to contradict with certainty, consider:

 DIRV* d = new DIRV;
 BASE* b = d;

 delete b; // ERROR!!!


IE a base-part subobject CANNOT uniformly be used as a BASE object,
thus the base-part CANNOT BE a BASE object.








Author: jimad@microsoft.com (Jim Adcock)
Date: Mon, 20 Jun 1994 19:11:50 GMT
Raw View
In article <pkt-160694102318@138.52.2.129> pkt@lpi.liant.com (Scott Turner) writes:
|"This notion" that a pointer points to an object of the dereferenced
|type of the pointer is plainly basic.

Whether the notion is basic or not, it is clearly contradicted by
the design of the language, and thus one must discard this 'basic'
notion for one more consistent with the actual design of the language.

Consider, trivially:

 FOO* foo = new FOO;  // I'm a foo!

 int* i = (int*)foo;

 *i = 1234;  // No, I'm an int!;

 foo.DoFooStuff(); // CRASH!!!


Again, I'd recommend that C++ simply recognize that the type of an
object be the type of the declaration that allocated storage for that
object.





Author: jimad@microsoft.com (Jim Adcock)
Date: Tue, 21 Jun 1994 18:54:11 GMT
Raw View
In article <CrMEt8.6I2@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
|In article <CrJyzB.EoL@microsoft.com> jimad@microsoft.com (Jim Adcock) writes:
|>I have yet to see any serious discussions of what an object is or isn't.
|>In "C" it was simple: an object was indeed a contiguous region of storage,
|>and the type of an object was simply the type of the pointer [or reference]
|>to that contiguous region of storage.  In "C" such an approach was pretty
|>necessary to allow dynamic creation of objects:
|>
|>FOO* fooarray = malloc(100 * sizeof(FOO);  /* an array of 100 FOOs */
|
| NO! In C the approach FAILS in exactly this case.
|The C object model is flawed, there are no heap objects.
|Such an "object" has no defined lifetime and its existence
|as an object is categorically denied by the ISO C Standard,
|which says an object has either static or automatic storage
|duration. Heap objects in C have NO duration. So no rules
|apply to them -- in effect they are not objects.

I cannot find quotes in the "C" documents to support your position.
Can you please quote what sections you claim support your position?
To support my position I quote:

4.10.3.3

"The malloc function allocates space for an OBJECT whose size is specified..."
[emphasis mine]

1.6 line 26

"Object -- a region of data storage in the execution environment, the
contents of which can represent values....When referenced, an object
may be interpreted as having a particular type;..."

So my claim is that malloc creates such a region of data storage, accessing
the region via a particular pointer type causes interpretation of that
storage as that type.

3.1.2.4 Storage Duration of Objects

"An object has a 'storage duration' that determines its lifetime.  There
are two storage durations: static and automatic."

I simply read this as saying that there are two *defined* durations of
objects.  Objects created by malloc do not have a defined duration --
they exist until freed.

|>Personally, I would opt for the C++ definition of an objects type
|>to simply be the declared type of the object, IE the type of the region of
|>storage, at construction, where all objects by definition have a
|>constructor, even if some constructors "do nothing".
|
| That is NOT the declared type. That is the type
|of the constructor of the object. That is my definition for
|"encapsulated objects" but it doesnt work for the plain old
|C objects.

"Works" for me.  It simply depends on how much "C" usage you are willing
to make 'implementation dependent' now that there are better, type-safe,
"C++" ways of doing the same job.

|>Then any attempt
|>to access a BAR as-if a FOO [FOO unrelated to BAR] simply becomes
|>implementation dependent.  Likewise the access of a float as an
|>array of chars, or vice versa.
|>
|>Such an approach would render most common C-hackery as strictly
|>implementation-dependent.  People claiming to write portable code
|>could then instead use type-safe features of C++.
|>
|
| They also could never use 'memcpy' to copy an object.

Sure they could.  Its simply that using memcpy to copy an object would
give you implementation defined behavior.

|Also, by "access" I presume you mean "read-access". In C++
|apart from write access there is also method call. What
|does that mean for a float?

I'm not sure of your question.  Floats before given a value have an
undefined value.  Accessing a float for read or write by other than
a float ref would be implementatin defined.




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 22 Jun 1994 22:52:26 GMT
Raw View
In article <CrpMo4.46H@microsoft.com> jimad@microsoft.com (Jim Adcock) writes:
>
>Again, I'd recommend that C++ simply recognize that the type of an
>object be the type of the declaration that allocated storage for that
>object.
>

"Declaration" is NOT good enough: malloc allocates storage.
That storage can be used to hold an object without any declarations
of objects (other than pointers).


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: jason@cygnus.com (Jason Merrill)
Date: Fri, 10 Jun 1994 20:28:34 GMT
Raw View
>>>>> Rohan LENARD <rjl@f111.iassf.easams.com.au> writes:

> There is another naughty non-portable way to change it, but it only works
> with global, or static references. eg. (for gcc)

Here is a way to do it for automatic references.  This is probably
ill-formed, but it works on some compilers:

main()
{
  union {
    int* p;
    int& r;
  };

  int i;
  p = &i;
  r = 5;
  return (i != 5);
}

Jason




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sat, 11 Jun 1994 20:30:24 GMT
Raw View
anthony_armenta@taligent.com (Anthony Armenta) writes:

>> >- The fact that a reference always points to a (once) valid memory localtion
>> >    (a pointer can be NULL).
>>
>>  misconception.
>>
>>  int* pi = 0;
>>  int& ri = *pi;
>
>The above comment is absolutely right.  It is a misconception to think
>that a reference always points to a legal object.

I agree that it's not true that a reference always points to a legal object.
For example, the following is quite legal:

 int *pi = new int;
 int &ri = *pi;
 delete pi;
 // now ri is a dangling reference

But in a legal program, a reference must be initialized by an object.
Initializing a reference using *(Foo *)NULL is not legal.

>Consider the following where T is a
>class:
>
>void f (T& t) { cout << t; }
>
>void g(T* s) { f (*s); }
>
>main () {
> T* r=0;
> g(r);
>}
>
>main() should be able to call g and pass T pointer (r) even if it refers to
>NULL.

Yes.

>g() has no
>way to tell at compile time if s points to NULL or real memory

Sure it does.  The compiler is allowed to generate code that dereferences
`s' to make sure that `s' isn't a null pointer.  In fact, the compiler
is quite free to generate code that does an explicit

 if (s == 0) abort();

before the call to f().

>but it must allow the call to f() with the object *s.

No, that's quite incorrect.

>This is a runtime problem, perfectly valid code.

That sentence is a contradiction.  Perfectly valid code doesn't
have runtime problems.  It's true that the compiler is not
obliged to report the problem at compile time, but if it smart
enough to discover that the problem will definitely occur, then
it can refuse to compile your program.  Initializing a reference
using *NULL is similar to dividing by zero: compilers are not
required to issue a diagnostic at compile-time, and the program
may even run just fine on some implementations.

In case you don't understand the meaning of UNDEFINED BEHAVIOUR,
try compiling the following piece of code with your favorite compiler.

 #include <stdlib.h>
 #include <assert.h>

 struct A { int a; };
 struct B { int b; };
 struct C : A, B {};

 B* f(C* pc) { return pc; }
 B& f(C& rc) { return rc; }

 int main() {
  C* null_ptr = NULL;
  B* pb = f(null_ptr);
  assert(pb == NULL);
  B& rb = f(*null_ptr);
  assert(&rb == NULL);
  return 0;
 }

If this code's use of *null_ptr were really legal, it would
exit successfully.  I tried it on four different C++ implementations:

 Implementation   Result
 --------------   ------
 g++ 2.5.8   Assertion #2 failed
 Cfront 2.0   Successful execution
 Cfront 3.0.2   Assertion #2 failed
 Centerline C++ Interpreter Runtime error ("pointer out of bounds")

Note that ALL of these results are allowed by the ARM and the latest
working paper.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sun, 12 Jun 1994 18:01:13 GMT
Raw View
In article <9416306.11540@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>
>>The above comment is absolutely right.  It is a misconception to think
>>that a reference always points to a legal object.
>
>I agree that it's not true that a reference always points to a legal object.
>For example, the following is quite legal:
>
> int *pi = new int;
> int &ri = *pi;
> delete pi;
> // now ri is a dangling reference
>
>But in a legal program, a reference must be initialized by an object.

 Oh? Well, I dont agree.  First, please say "bound"
not "initialised". Second, as far as I can see,
a reference has to be bound to valid store (or perhaps 0)
but thats the end of it: the store doesnt have to contain
an object.

For example, I contend
that BEFORE the delete pi above, ri does not refer to an object.
Thats because '*new int' isnt an object because the allocated
store isnt initialised.

 Now, there is a distinct difference between a reference
having to refer to allocated memory (i.e. be a valid address)
and having to refer to an object. How about:

 char buffer[20]; // not initialised. an object??
 float &ref = *(float*)(void*)buffer;

Well, that refers to allocated memory, but no 'float' object.
Floats surely have to be initialised and not tampered with
to become and remain objects.


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: doug@monet.ads.com (Doug Morgan)
Date: 13 Jun 94 10:28:19
Raw View
In article <CrAq21.Cxr@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
: ...
:For example, I contend
:that BEFORE the delete pi above, ri does not refer to an object.
:Thats because '*new int' isnt an object because the allocated
:store isnt initialised.
:
:Now, there is a distinct difference between a reference
:having to refer to allocated memory (i.e. be a valid address)
:and having to refer to an object. How about:
:
:char buffer[20]; // not initialised. an object??
:float &ref = *(float*)(void*)buffer;
:
:Well, that refers to allocated memory, but no 'float' object.
:Floats surely have to be initialised and not tampered with
:to become and remain objects.

I don't think so.  The ARM states (multiple times) that "An object is
a region of storage."  Period.  However, this is one of several areas
in which the ARM seems eager either to contradict itself or (more
charitably) to add nuances to what appear to be complete and
unqualified definitions.

Doug
--------------------------------------------------------------------
Doug Morgan, doug@ads.com, (415) 960-7444
Advanced Decision Systems (a division of Booz-Allen & Hamilton Inc.)
1500 Plymouth St., Mountain View, CA 94043-1230
--------------------------------------------------------------------




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 13 Jun 1994 18:08:38 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>>
>>But in a legal program, a reference must be initialized by an object.
>
> Oh? Well, I dont agree.  First, please say "bound"
>not "initialised".  Second, as far as I can see,
>a reference has to be bound to valid store (or perhaps 0)
>but thats the end of it: the store doesnt have to contain
>an object.

Yes, I agree with all of this except the "(or perhaps 0)".  I was
paraphrasing ARM 8.4.3, which is based on a rather primitive notion of
"object" from ARM 3.7: "An _object_ is a region of storage".  So what I
meant was just that in a legal program, a reference must be bound to a
valid region of storage.  The storage does not have to be initialized.
But a reference can't be bound to a null pointer, as I hope my example
program illustrated.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 15 Jun 1994 11:10:17 GMT
Raw View
In article <DOUG.94Jun13102819@monet.ads.com> doug@monet.ads.com (Doug Morgan) writes:
>
>I don't think so.  The ARM states (multiple times) that "An object is
>a region of storage."  Period.

 That does not imply every region of storage is an object,
only that an object has the attribute of having an associated
region of storage -- I dont think the ARM is (necessarily) wrong,
the definition is just not complete. The IS an argument
that empty base classes are objects but dont constitute a
"region" of storage but that, while important in itself,
doesnt discount the ARM notion -- its just inadequate.

>However, this is one of several areas
>in which the ARM seems eager either to contradict itself or (more
>charitably) to add nuances to what appear to be complete and
>unqualified definitions.

 Yep. The WP has this problem too.
--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: jimad@microsoft.com (Jim Adcock)
Date: Fri, 17 Jun 1994 17:52:21 GMT
Raw View
In article <DOUG.94Jun13102819@monet.ads.com> doug@monet.ads.com (Doug Morgan) writes:
>In article <CrAq21.Cxr@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
|:char buffer[20]; // not initialised. an object??
|:float &ref = *(float*)(void*)buffer;
|:
|:Well, that refers to allocated memory, but no 'float' object.
|:Floats surely have to be initialised and not tampered with
|:to become and remain objects.
|
|I don't think so.  The ARM states (multiple times) that "An object is
|a region of storage."  Period.  However, this is one of several areas
|in which the ARM seems eager either to contradict itself or (more
|charitably) to add nuances to what appear to be complete and
|unqualified definitions.

I have yet to see any serious discussions of what an object is or isn't.
In "C" it was simple: an object was indeed a contiguous region of storage,
and the type of an object was simply the type of the pointer [or reference]
to that contiguous region of storage.  In "C" such an approach was pretty
necessary to allow dynamic creation of objects:

FOO* fooarray = malloc(100 * sizeof(FOO);  /* an array of 100 FOOs */

In the presence of virtual function pointers, and virtual base pointers
in C++, such a definition is no longer generally sensible.  You could
still retain the "C" definition for only those subset of objects that
are "C" objects, IE primitives and struct/class objects without ctors,
dtors, vtptrs or vbptrs.

However the "C" approach clearly breaks down given C++ objects with
vtptrs and/or vbptrs.  Virtual bases and multiple inheritence leads
to base-part objects that need not be contiguous, but which can have
regions of other base-objects embedded in them.  And given virtual
pointers:

 BAR bar;

 FOO& foo = (FOO&)bar;

 foo.doFooStuff(); // CRASH!!!

Leads to programming crashes, and crashing behavior clearly should be
inconsistent with program conformity.

Personally, I would opt for the C++ definition of an objects type
to simply be the declared type of the object, IE the type of the region of
storage, at construction, where all objects by definition have a
constructor, even if some constructors "do nothing".  Then any attempt
to access a BAR as-if a FOO [FOO unrelated to BAR] simply becomes
implementation dependent.  Likewise the access of a float as an
array of chars, or vice versa.

Such an approach would render most common C-hackery as strictly
implementation-dependent.  People claiming to write portable code
could then instead use type-safe features of C++.





Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Sat, 18 Jun 1994 06:38:33 GMT
Raw View
In article <9415917.16677@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>rfg@netcom.com (Ronald F. Guilmette) writes:
>
>>rjl@f111.iassf.easams.com.au (Rohan LENARD) writes:
>>>>>  int* pi = 0;
>>>>>  int& ri = *pi;
>[...]
>>>A language lawyer could say it is illegal since -
>>>
>>>ARM 8.4.3 References, (pg. 153)
>>>  "A variable declared to be a _T&_, this is "reference to type _T_"
>>>   (%8.2.2) must be initialized by an object of type _T_ or by an object
>>>   that can be converted into a _T_."
>[...]
>>>Thus since the null pointer constant does not point at any object, the
>>>code is violating the constraint specified by 8.4.3 (that it must be an
>>>object). Hence the code is illegal.
>>
>>As far as I know, there are no such things as ``constraints'', either in the
>>ARM or in the most recent ANSI/ISO C++ standardization draft.
>
>Try looking it up in the dictionary.  "constraint" here is used
>in it's informal English meaning.  Just because the working draft
>doesn't define "constraint" doesn't mean that it doesn't contain any.

But you see, this is exactly the problem I was attempting to call attention
to.  An international language standard should not be reliant upon unstated
definitions and/or informal understandings.  Things should be spelled out
plainly.  Alas, due to extensive wrangling within the committee, much time
was wasted in debating the merits of the ANSI/ISO C standard's more formalized
(and more well established and tested) notion of ``constraint'', and as far
as I can see, the sum total of all of this wrangling is that unlike the C
standard, which contains ``constraints'' of the form:

 An X shall not Y.

the C++ standard will instead contain more verbose (but functionally identical)
statements of the form:

 A translation unit containing an X that does/has Y is ill-formed.

A really complete set of these X3J16/WG21 style ``constraints'' has yet to
be written into the evolving draft working papers.  My own feeling is that
this sad fact is due (at least in part) to the vast number of other things
that the committees (and the editors) have been occupied with (and that
among these has been (a) the pointless wrangling over which form is better,
and (b) the additional typing required by the latter form).

All in all, I believe that this is a clear case were the committee (and
those anxiously awaiting a *finished* standard) would have been far better
off to leave well-enough alone, and just reuse the exact same notion (and
form) of ``constraints'' which had already proven its merits in one of
the base documents for the C++ standard... i.e. the ISO C standard.

(I have never figured out exactly why the committee has shown such reluctance
to ``reuse'' either concepts or verbage from the existing C standard.  I
supposed it's the same reason that most programmers want to write programs
from scratch, rather than trying to make any effort to reuse existing
pre-tested components.)

>>Thus, your
>>assertion that the code is in some sense ``illegal'' may be unwarranted.
>
>No, I disagree.  The code quite clearly breaks a rule specified in the
>ARM (and no doubt also the latest working draft).  What more evidence
>could you want before proclaiming it to be illegal?

Well, now we have to get into discussing some *additional* (different)
kinds of hair-splitting.  Specifically, what do you mean by ``illegal''?
Do you mean that all ``conformant'' compilers must issue an error for
such usage?  Or do you merely mean that such usage can result in ``undefined
behavior''?

Note that unless the current working paper of the C++ standardization
committee EXPLICITLY says that doing such-and-such causes your program
(or translation unit) to be ``ill-formed'', there is no requirement that
any conformant compiler must issue a diagnostic if you actually do put
such-and-such in your code.

This is a rather important distinction.  There is illegal and then there
is ILLEGAL.

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- domain addr: rfg@netcom.com ----------- Purveyors of Compiler Test ----
---- uucp addr: ...!uunet!netcom!rfg ------- Suites and Bullet-Proof Shoes -




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sun, 19 Jun 1994 01:29:31 GMT
Raw View
In article <CrJyzB.EoL@microsoft.com> jimad@microsoft.com (Jim Adcock) writes:
>In article <DOUG.94Jun13102819@monet.ads.com> doug@monet.ads.com (Doug Morgan) writes:
>|
>|I don't think so.  The ARM states (multiple times) that "An object is
>|a region of storage."  Period.  However, this is one of several areas
>|in which the ARM seems eager either to contradict itself or (more
>|charitably) to add nuances to what appear to be complete and
>|unqualified definitions.
>
>I have yet to see any serious discussions of what an object is or isn't.
>In "C" it was simple: an object was indeed a contiguous region of storage,
>and the type of an object was simply the type of the pointer [or reference]
>to that contiguous region of storage.  In "C" such an approach was pretty
>necessary to allow dynamic creation of objects:
>
>FOO* fooarray = malloc(100 * sizeof(FOO);  /* an array of 100 FOOs */

 NO! In C the approach FAILS in exactly this case.
The C object model is flawed, there are no heap objects.
Such an "object" has no defined lifetime and its existence
as an object is categorically denied by the ISO C Standard,
which says an object has either static or automatic storage
duration. Heap objects in C have NO duration. So no rules
apply to them -- in effect they are not objects.
>
>Personally, I would opt for the C++ definition of an objects type
>to simply be the declared type of the object, IE the type of the region of
>storage, at construction, where all objects by definition have a
>constructor, even if some constructors "do nothing".

 That is NOT the declared type. That is the type
of the constructor of the object. That is my definition for
"encapsulated objects" but it doesnt work for the plain old
C objects.

>Then any attempt
>to access a BAR as-if a FOO [FOO unrelated to BAR] simply becomes
>implementation dependent.  Likewise the access of a float as an
>array of chars, or vice versa.
>
>Such an approach would render most common C-hackery as strictly
>implementation-dependent.  People claiming to write portable code
>could then instead use type-safe features of C++.
>

 They also could never use 'memcpy' to copy an object.
Also, by "access" I presume you mean "read-access". In C++
apart from write access there is also method call. What
does that mean for a float?

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sun, 19 Jun 1994 15:13:44 GMT
Raw View
rfg@netcom.com (Ronald F. Guilmette) writes:

>fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>rfg@netcom.com (Ronald F. Guilmette) writes:
>>
>>>As far as I know, there are no such things as ``constraints'', either in the
>>>ARM or in the most recent ANSI/ISO C++ standardization draft.
[...]
>An international language standard should not be reliant upon unstated
>definitions and/or informal understandings.

This is true.  It is however irrelevant to the earlier topic of this
thread, which was asking about the legality of a particular piece of
code.

>>>Thus, your
>>>assertion that the code is in some sense ``illegal'' may be unwarranted.
>>
>>No, I disagree.
[...]
>Well, now we have to get into discussing some *additional* (different)
>kinds of hair-splitting.  Specifically, what do you mean by ``illegal''?
>Do you mean that all ``conformant'' compilers must issue an error for
>such usage?  Or do you merely mean that such usage can result in ``undefined
>behavior''?

The latter.

>This is a rather important distinction.  There is illegal and then there
>is ILLEGAL.

Sure.  I thought it would have been fairly clear from the context that
I meant `illegal' rather than `ILLEGAL' ;-)

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: rjl@f111.iassf.easams.com.au (Rohan LENARD)
Date: 16 Jun 1994 07:22:04 +1000
Raw View
In article <CrFr16.3B6@ucc.su.oz.au>,
John Max Skaller <maxtal@physics.su.OZ.AU> wrote:
[ ..snip.. ]
John> The IS an argument
John>that empty base classes are objects but dont constitute a
John>"region" of storage but that, while important in itself,
John>doesnt discount the ARM notion -- its just inadequate.
John>

Doesn't the ARM also state that the sizeof(foo) != 0, so all objects (even
empty ones) must have a "region" of storage associated with them

Regards,
 Rohan
--
------------------------------------------------------------------------
rjl@iassf.easams.com.au |
Rohan Lenard            |                .sig on holiday
+61-2-367-4555          |




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 15 Jun 1994 23:36:14 GMT
Raw View
In article <2tnrds$q5c@f111.iassf.easams.com.au> rjl@f111.iassf.easams.com.au (Rohan LENARD) writes:
>
>Doesn't the ARM also state that the sizeof(foo) != 0, so all objects (even
>empty ones) must have a "region" of storage associated with them

 1) sizeof(foo) is defined to be the size an object would occupy
 in an array. You cant PUT an abstract object into an array!
 Therefore sizeof(foo) is undefined for abstract class foo.

 2) Even if n==sizeof(foo) is non zero that does not mean
 that a foo base subobject has size n. It does not
 even mean that a member has size n, since a member subobject
 may or may not be an object.

 A base class subobject almost certainly is NOT an object:
 they certainly dont have to occupy "contiguous" regions
 of store (and any other notion of region is absurd).

 Note that it follows that a pointer "to a base class subobject"
 is not a pointer "to an object of the dereferenced type of the
 pointer", so much of the ARM written with this notion behind
 it is incorrect.

 Until a definitive object model is accepted, its hard to
 say what is what.



--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: rjl@f111.iassf.easams.com.au (Rohan LENARD)
Date: 16 Jun 1994 11:39:42 +1000
Raw View
In article <CrGpKE.Hqz@ucc.su.oz.au>,
John Max Skaller <maxtal@physics.su.OZ.AU> wrote:
>In article <2tnrds$q5c@f111.iassf.easams.com.au> rjl@f111.iassf.easams.com.au (Rohan LENARD) writes:
>>
>>Doesn't the ARM also state that the sizeof(foo) != 0, so all objects (even
>>empty ones) must have a "region" of storage associated with them
>
> 1) sizeof(foo) is defined to be the size an object would occupy
> in an array. You cant PUT an abstract object into an array!
> Therefore sizeof(foo) is undefined for abstract class foo.
>
Ok I agree for abstract classes, however trivial ones (eg.)

class foo {};

will have a size from sizeof of 1, but most implementations will give it
a stored size (when used as part of a specialisation) of 0, however once it
is non-trivial (eg. virtual functions are defined) it generally gets a size
which is equal to the sizeof size.

I think section 5.3.2 Sizeof (from the ARM) is a little ambiguous, because it
says -
  "The size of any class or class object is larger than zero"

I agree its written in the context of describing the sizeof operator, but
the wording doesn't make it obvious exactly whether it applies only to
the sizeof operator or to both objects and the operator.

> 2) Even if n==sizeof(foo) is non zero that does not mean
> that a foo base subobject has size n. It does not
> even mean that a member has size n, since a member subobject
> may or may not be an object.
>
> A base class subobject almost certainly is NOT an object:
> they certainly dont have to occupy "contiguous" regions
> of store (and any other notion of region is absurd).
>
> Note that it follows that a pointer "to a base class subobject"
> is not a pointer "to an object of the dereferenced type of the
> pointer", so much of the ARM written with this notion behind
> it is incorrect.
>
Why not.  It is quite valid (though patently not very useful if the derived
object is used for this purpose only), to treat the base class subobject as
an object of the base class.
Sure the complete object isn't a base class type object.
However (in the absence of virtual functions) the extra functionality
provided by the specialisation is just 'padding' within the context of
its use as a base class object.
It doesn't matter that the derived object is a kind of object.

> Until a definitive object model is accepted, its hard to
> say what is what.
>
Has one been proposed ?


Regards,
 Rohan
--
------------------------------------------------------------------------
rjl@iassf.easams.com.au |
Rohan Lenard            |                .sig on holiday
+61-2-367-4555          |




Author: pkt@lpi.liant.com (Scott Turner)
Date: Thu, 16 Jun 1994 14:23:18 GMT
Raw View
In article <CrGpKE.Hqz@ucc.su.OZ.AU>, maxtal@physics.su.OZ.AU (John Max
Skaller) wrote:

>  A base class subobject almost certainly is NOT an object:
>  they certainly dont have to occupy "contiguous" regions
>  of store (and any other notion of region is absurd).
>
>  Note that it follows that a pointer "to a base class subobject"
>  is not a pointer "to an object of the dereferenced type of the
>  pointer", so much of the ARM written with this notion behind
>  it is incorrect.

"This notion" that a pointer points to an object of the dereferenced
type of the pointer is plainly basic.  Contiguity of objects
is a representational issue, and so ought to be subordinate.

Your reasoning is not persuasive.  You should go back to the drawing
board, rather than overthrow a fundamental.
--
Scott Turner
Liant Software Corp., Framingham, Mass., USA
pkt@lpi.liant.com




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Thu, 16 Jun 1994 20:52:03 GMT
Raw View
In article <2toagu$4kf@f111.iassf.easams.com.au> rjl@f111.iassf.easams.com.au (Rohan LENARD) writes:
>
>I think section 5.3.2 Sizeof (from the ARM) is a little ambiguous, because it
>says -
>  "The size of any class or class object is larger than zero"

 Understatement of the year. The ARM here is clear
that sizeof(X)>0 for a class X. The other bit is waffle,
since not only is the notion of object not defined but is
being used in the wrong context anyhow. You cant, and never
could, and never will be able to, apply the sizeof, or any other
operator, to an object.

 Objects are run time entities. You can apply the sizeof
operator, and other operators, to EXPRESSIONS. And an expression
is categorically distinct from an object.

 In particular "variable" is the word I think ought to be
used almost everywhere the ARM gets confused and says "object".
In the case of members, "data member" might be applicable,
again a data member is not an object.

 Its true a variable will be associated with an object,
or more than one object, during some extent of time during
execution and its exactly because much of the language description
is there to explain that relationship that the confusion between
"object" and "variable" is such a serious error.
>
>Why not.  It is quite valid (though patently not very useful if the derived
>object is used for this purpose only), to treat the base class subobject as
>an object of the base class.

 Definitely not until the relationship is explained
exactly. Whether your statement is true or not depends on
the definition of "object".

>Sure the complete object isn't a base class type object.

 Exactly. They have different properties.
And a base class is NOT a class. Repeat: its NOT a class nor a type.
The terminology is confusing. Consider that a base may be virtual:
that is not a property of a type or class, it _is_ a property
of a base class. A base class subobject, therefore, is not
an object of the type of the base. If it were, how would
polymorphism be explained?

>> Until a definitive object model is accepted, its hard to
>> say what is what.
>>
>Has one been proposed ?

 No. I'm working on one with assistance from Bill Plauger.
Josee Lajoie is also working on one. Tom Plum is also working
in the area of the object model (there are surely others as well).
There are some substantive issues, but much of the work is
an editorial matter of a precise specification where either
none exists or what exists is plainly inadequate.

 The job is relatively straightforward but non-trivial:
the C model is inadequate for C++ (which is object oriented :-)
[The C model is also not really adequate for C either :-[

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: rjl@f111.iassf.easams.com.au (Rohan LENARD)
Date: 9 Jun 1994 10:01:24 +1000
Raw View
Hi there folks,

In article <KANZE.94May31150520@slsvhdt.us-es.sel.de>,
James Kanze <kanze@us-es.sel.de> wrote:
>In article <2s8u3b$dtp@news.iastate.edu> guthrie@miu.edu writes:
>
>|> In article <CqIppE.FqD@ucc.su.OZ.AU>, <maxtal@physics.su.OZ.AU> writes:
>
>|> > In article <2s5dur$4ut@news.iastate.edu> guthrie@miu.edu writes:
>
>|> > >3) What I would REALLY like is mutable references;
>
>|> >  You already have them:
>
>|> >  struct X {
>|> >   Y& y;
>|> >   X(Y& yy) : y(yy) {}
>|> >  };
>
>|> >  Y y1, y2;
>|> >  X x(y1);
>|> >  ...
>|> >  x.~X();
>|> >  new(&x) X(y2);
>|> >  ...
>


I just realised the code posted by John (MAX) Skaller could NEVER work.
The reason is given in the following example in the ARM.


int i = 1;
int &r = i;
int *p = &r;    // 'p' points to 'i'


Since it is not possible to have a pointer to a reference, it is not
possible to use the placement operator new, to re-initialise the
reference, so I don't believe mutable references are possible..



Regards,
        Rohan

--
------------------------------------------------------------------------
rjl@iassf.easams.com.au |
Rohan Lenard            |                .sig on holiday
+61-2-367-4555          |




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Thu, 9 Jun 1994 02:37:25 GMT
Raw View
In article <2t5m4k$56@f111.iassf.easams.com.au> rjl@f111.iassf.easams.com.au (Rohan LENARD) writes:
>Hi there folks,
>
>In article <KANZE.94May31150520@slsvhdt.us-es.sel.de>,
>James Kanze <kanze@us-es.sel.de> wrote:
>>In article <2s8u3b$dtp@news.iastate.edu> guthrie@miu.edu writes:
>>
>>|> In article <CqIppE.FqD@ucc.su.OZ.AU>, <maxtal@physics.su.OZ.AU> writes:
>>
>>|> > In article <2s5dur$4ut@news.iastate.edu> guthrie@miu.edu writes:
>>
>>|> > >3) What I would REALLY like is mutable references;
>>
>>|> >  You already have them:
>>
>>|> >  struct X {
>>|> >   Y& y;
>>|> >   X(Y& yy) : y(yy) {}
>>|> >  };
>>
>>|> >  Y y1, y2;
>>|> >  X x(y1);
>>|> >  ...
>>|> >  x.~X();
>>|> >  new(&x) X(y2);
>>|> >  ...
>>
>
>
>I just realised the code posted by John (MAX) Skaller could NEVER work.
>The reason is given in the following example in the ARM.
>
>
>int i = 1;
>int &r = i;
>int *p = &r;    // 'p' points to 'i'
>
>
>Since it is not possible to have a pointer to a reference, it is not
>possible to use the placement operator new, to re-initialise the
>reference, so I don't believe mutable references are possible..

 Your logic is correct but doesnt apply to the example.
I dont use the placement new on a reference but a class object
containing one. x is an X, it is x.y that is the reference.
(But you had me convinced for a while :-)


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: rjl@f111.iassf.easams.com.au (Rohan LENARD)
Date: 9 Jun 1994 16:07:24 +1000
Raw View
Hi there folks,

In article <Cr3zAD.1FA@ucc.su.oz.au>,
John Max Skaller <maxtal@physics.su.OZ.AU> wrote:
>In article <2t5m4k$56@f111.iassf.easams.com.au> rjl@f111.iassf.easams.com.au (Rohan LENARD) writes:
>>Hi there folks,
>>
>>In article <KANZE.94May31150520@slsvhdt.us-es.sel.de>,
>>James Kanze <kanze@us-es.sel.de> wrote:
>>>In article <2s8u3b$dtp@news.iastate.edu> guthrie@miu.edu writes:
>>>
>>>|> In article <CqIppE.FqD@ucc.su.OZ.AU>, <maxtal@physics.su.OZ.AU> writes:
>>>
>>>|> > In article <2s5dur$4ut@news.iastate.edu> guthrie@miu.edu writes:
>>>
>>>|> > >3) What I would REALLY like is mutable references;
>>>
>>>|> >  You already have them:
>>>
>>>|> >  struct X {
>>>|> >   Y& y;
>>>|> >   X(Y& yy) : y(yy) {}
>>>|> >  };
>>>
>>>|> >  Y y1, y2;
>>>|> >  X x(y1);
>>>|> >  ...
>>>|> >  x.~X();
>>>|> >  new(&x) X(y2);
>>>|> >  ...
>>>
>>
>>
>>I just realised the code posted by John (MAX) Skaller could NEVER work.
>>The reason is given in the following example in the ARM.
>>
>>
>>int i = 1;
>>int &r = i;
>>int *p = &r;    // 'p' points to 'i'
>>
>>
>>Since it is not possible to have a pointer to a reference, it is not
>>possible to use the placement operator new, to re-initialise the
>>reference, so I don't believe mutable references are possible..
>
> Your logic is correct but doesnt apply to the example.
>I dont use the placement new on a reference but a class object
>containing one. x is an X, it is x.y that is the reference.
>(But you had me convinced for a while :-)
>


Yes I realised this about five minutes after I posted :-(
However it's a still a bit of a hack, because it essentially means that
if you want a mutable reference you have to enclose the reference in
a class (generically speaking).


There is another naughty non-portable way to change it, but it only works
with global, or static references. eg. (for gcc)


class foo {
  int a;
};

class Miow {
  static foo& fRep;
};

extern foo AGlobalFoo;
foo AGlobalFoo;

foo& Miow::fRep = AGlobalFoo;
extern _4Miow$fRep;

main()
{
  foo YY;
  _4Miow$fRep = &YY;    // Change reference to YY

  // Do something

  foo ZZ;

  _4Miow$fRep = &ZZ;    // Change reference to ZZ
}


At least this way it's obvious that something quite strange is going on :-)


Regards,
--
------------------------------------------------------------------------
rjl@iassf.easams.com.au |
Rohan Lenard            |                .sig on holiday
+61-2-367-4555          |




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Thu, 9 Jun 1994 13:30:05 GMT
Raw View
In article <2t6bis$hr6@f111.iassf.easams.com.au> rjl@f111.iassf.easams.com.au (Rohan LENARD) writes:

[struct containing a refernece re-initialised by explicit destruction
and re-construction ....]

>
>However it's a still a bit of a hack,

 The polite term is "idiom" :-))

>because it essentially means that
>if you want a mutable reference you have to enclose the reference in
>a class (generically speaking).

 Yes. Its cheating really.
--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: fjh@mundil.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 6 Jun 1994 09:16:05 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

> In C one cannot have:
>
> extern int i;
> int & j = i;
  -- of course not, because it's C++!
> int* pi = &i;
> j;
> *pi;
  -- because it accesses uninitialized memory

>Both "j" and "*pi" are the same lvalue, and both refer to
>the same uninitialised store, and both do nothing
>except perhaps load the address of the store.

No, they should load the value of the store, not just it's address.

>Neither requires an object to exist. Obviously because:
>
> j = 1;
> *pi = 1;
>
>can be used to actually create the object.

But this is different - now you are using the expressions
in an lvalue context, not an rvalue context.

> This is a distinct change from C -- in C
>each use of an expression denoted a VALUE (as well
>as possible an address if the expression were an lvalue).
>
> But in C++ this is not the case. Mere appearance
>of a name does not denote a value -- C++ is object oriented
>and there is no such thing as a value, at least for classes.

Then perhaps that is the problem.  Perhaps C++ *should*
have a concept of value, even for classes.  (See below.)

>It would be VERY DANGEROUS to make the behaviour of
>a reference and a dereferenced pointer different.

Yes, I agree.

> A reference MAY be bound to store not yet containing
>an initialised object in C++ as far as I know.

Yes, this is definitely necessary.

>Use of such a reference, whether it be a name declared as a reference
>or a dereferenced pointer does NOT constitute access to the
>storage or use of a value.

That depends on the context.  If the reference is used in an lvalue
context, then no it doesn't.  But if it is used in an rvalue context,
then yes it does.

>Now, the question is whether we should invent
>any SPECIAL rules for the NULL pointer: it is by definition
>a valid address even though it does not refer to any store.
>
> Quite simply I feel that if no attempt is made
>to access the (non-existant) store to which the address
>points then there is no error.

I think that if one were designing a language from scratch, that would
be a quite reasonable position to take.  But there are too many
textbooks out there which say that in C++, a reference may not
refer to a null pointer.  There are too many optimizing compilers
which make optimizations assuming that references are never null
(e.g. when converting from a reference to a derived class to a reference
to a base class where MI requires that the pointer be adjusted.)

> Dereferencing a null pointer does not cause
>a read or write to the non-existant store. So it should
>be legal. In particular:
>
> char * p = NULL;
> char * q = &*p; // q == NULL
>
>looks just fine to me

Sure, it looks fine to me too, since `*p' occurs as the operand of `unary &',
i.e. in an lvalue context, not in an rvalue context.

>You cant say "the value is fetched and thrown away"
>in C++ because C++ is object oriented and there ARE NO VALUES
>to fetch: you CANT fetch the value of a class, only copy it
>from one object to another.

I beg to differ.  I think it makes sense to give a meaning to "fetching
the value" of a class object.  In particular, I think it makes sense to
consider "fetching the value" of an object to mean constructing a
temporary unnamed copy using the copy-constructor, and then destroying it.

Consider the statement

 func();

where `func()' has been defined as

 String func() {
  String x("blah");
  return x;
 }

i.e. as a function returning a class object.
The effect of this statement is to

 (1) Invoke the func() funciton
 (2) Construct a String object `x' using the String(const char *)
     constructor.
 (3) Construct an unnamed temporary String object using the
     String copy-constructor.
 (4) Destroy `x'
 (5) Destroy the unnamed temporary

except that compilers are allowed to optimize away the pair of steps
(3) and (5).  According to my terminology, steps (3) and (5) constitute
fetching the return value of the function.  Similarly, it would make
sense for a statement

 x;

to create a temporary unnamed copy of x and then destroy it, although
compilers could be allowed to optimize that away in the same way
that they are allowed to optimize it away for a function call.

>My opinion is that C types should
>follow the same model as classes for consistency

Yes, I agree, which is why I have suggested the above semantics for
"fetching the value" of an object, as required in an rvalue context.

>> int main() {
>>  int x;
>>  x;  // illegal C
>>    // also illegal C++, I suggest
>> }
>
> Thats weird. "x;" is useless but I dont see why
>its illegal :-)

Because conceptually it constitutes an access to uninitialized storage.
Sure, most compilers will optimize that access away, but that's no
the point.  Compilers aren't *required* to optimize such accesses away.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: fjh@mundil.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 6 Jun 1994 12:42:05 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>
>>it would potentially break lots of library routines which
>>assume that references do not refer to null pointers.
>
> Not really. Those routines, just like others,
>have pre-conditions. That a reference -- or pointer for that matter --
>is not "null" is just a pre-condition.

Yes, but good libraries will document the pre-conditions on their
routines.  By allowing null references, you will have just broken
all the documentation, since the documented pre-conditions are not
strict enough anymore.

> What it comes down to is this: banning null references
>has no effect on diagnosis. But it renders some programs
>that would otherwise be perfectly well formed ill formed
>(gratuitously breaking sensible programs)
>and there is no gain at all in adding a non-diagnosable constraint,

There is an optimization gain.  Compilers are allowed to
assume that the constraint holds and optimize accordingly.
That can be significant in the case of conversions in
multiple-inheritance hierarchies.  Compilers already implement
such optimizations, and removing these optimizations could be difficult.

> Do you follow?
> Did I mess up the argument?

Yes to both ;-)

> Reason above: the convention has no advantages being
>required by the Standard, but it does have disadvantages.

I think you overestimate the disadvantages and underestimate
the advantages.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: barmar@think.com (Barry Margolin)
Date: 7 Jun 1994 18:32:40 GMT
Raw View
In article <rfgCqynBI.Iyp@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>In article <2sh64t$qks@f111.iassf.easams.com.au> rjl@f111.iassf.easams.com.au (Rohan LENARD) writes:
>>Thus since the null pointer constant does not point at any object, the
>>code is violating the constraint specified by 8.4.3 (that it must be an
>>object). Hence the code is illegal.
>
>As far as I know, there are no such things as ``constraints'', either in the
>ARM or in the most recent ANSI/ISO C++ standardization draft.  Thus, your
>assertion that the code is in some sense ``illegal'' may be unwarranted.

Since C++ standards committees don't use the word "constraint" in any
special way (so there are no constraints on the use of the word
"constraint"), wouldn't it be reasonable to interpret Rohan's statement as
using the word in its conventional, English-language sense?  The definition
of a constraint is "a limitation or restriction."  By that definition, when
the ARM says that a program "must" use some construct in a limited set of
ways, it's a constraint.  A program that uses the construct in some other
way violates the constraints, and doesn't conform to the _de_facto_
standard.

--
Barry Margolin
System Manager, Thinking Machines Corp.

barmar@think.com          {uunet,harvard}!think!barmar




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Wed, 8 Jun 1994 07:57:12 GMT
Raw View
rfg@netcom.com (Ronald F. Guilmette) writes:

>rjl@f111.iassf.easams.com.au (Rohan LENARD) writes:
>>>>  int* pi = 0;
>>>>  int& ri = *pi;
[...]
>>A language lawyer could say it is illegal since -
>>
>>ARM 8.4.3 References, (pg. 153)
>>  "A variable declared to be a _T&_, this is "reference to type _T_"
>>   (%8.2.2) must be initialized by an object of type _T_ or by an object
>>   that can be converted into a _T_."
[...]
>>Thus since the null pointer constant does not point at any object, the
>>code is violating the constraint specified by 8.4.3 (that it must be an
>>object). Hence the code is illegal.
>
>As far as I know, there are no such things as ``constraints'', either in the
>ARM or in the most recent ANSI/ISO C++ standardization draft.

Try looking it up in the dictionary.  "constraint" here is used
in it's informal English meaning.  Just because the working draft
doesn't define "constraint" doesn't mean that it doesn't contain any.

>Thus, your
>assertion that the code is in some sense ``illegal'' may be unwarranted.

No, I disagree.  The code quite clearly breaks a rule specified in the
ARM (and no doubt also the latest working draft).  What more evidence
could you want before proclaiming it to be illegal?

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: kanze@us-es.sel.de (James Kanze)
Date: 31 May 1994 14:05:20 GMT
Raw View
In article <2s8u3b$dtp@news.iastate.edu> guthrie@miu.edu writes:

|> In article <CqIppE.FqD@ucc.su.OZ.AU>, <maxtal@physics.su.OZ.AU> writes:

|> > In article <2s5dur$4ut@news.iastate.edu> guthrie@miu.edu writes:

|> > >3) What I would REALLY like is mutable references;

|> >  You already have them:

|> >  struct X {
|> >   Y& y;
|> >   X(Y& yy) : y(yy) {}
|> >  };

|> >  Y y1, y2;
|> >  X x(y1);
|> >  ...
|> >  x.~X();
|> >  new(&x) X(y2);
|> >  ...

|> John, This is very clever!
|>    However, I would rather have it builtin to the language;
|> The usage and syntax here obfuscate the underlying semantics, rather
|> like using call-by-pointer to simulate call-by-reference in C.
|> I thnk it is impressive that the underlying mechanisms are adequate to do
|> this.

What is the difference between a mutable reference and a pointer?
--
James Kanze                       email: kanze@lts.sel.alcatel.de
GABI Software, Sarl., 8 rue du Faisan, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                   -- Beratung in industrieller Datenverarbeitung




Author: rjl@f111.iassf.easams.com.au (Rohan LENARD)
Date: 1 Jun 1994 15:25:49 +1000
Raw View
In article <2sh0se$5dn@arcturus.ciril.fr>,
Olivier Galibert aka Sarayan <galibero@mines.u-nancy.fr> wrote:
>In article <Cqp31o.33D@ucc.su.OZ.AU>, maxtal@physics.su.OZ.AU (John Max Skaller)
>writes:
>> [...]
>>  int* pi = 0;
>>  int& ri = *pi;
>
>I really wonder what the standard says about this. I think it says
>NULL cannot be dereferenced, or that it has undefined behaviour, but
>it that a dereference ?
>
>  Sarayan

A language lawyer could say it is illegal since -

ARM 8.4.3 References, (pg. 153)
  "A variable declared to be a _T&_, this is "reference to type _T_"
   (%8.2.2) must be initialized by an object of type _T_ or by an object
   that can be converted into a _T_."

and

ARM 5.3 Unary Operators, (pg. 55)
  "The unary _*_ operator means 'indirection': the expression must be a
   pointer, and the result is an lvalue referring to the OBJECT to which
   the expression points."

Thus since the null pointer constant does not point at any object, the
code is violating the constraint specified by 8.4.3 (that it must be an
object). Hence the code is illegal.

Both the compilers I tried allow this code, with the predictable core
dump :-).  (gcc and cfront).
The info page for gcc, however, says that it does check for non-null
objects when used with references, but it doesn't seem to do anything.


Regards,
 Rohan
--
------------------------------------------------------------------------
rjl@iassf.easams.com.au |
Rohan Lenard            |                .sig on holiday
+61-2-367-4555          |




Author: galibero@mines.u-nancy.fr (Olivier Galibert aka Sarayan)
Date: 31 May 1994 17:56:05 GMT
Raw View
In article <CqoF5q.xC@ucc.su.OZ.AU>, maxtal@physics.su.OZ.AU (John Max Skaller)
writes:
> In article <KANZE.94May31150520@slsvhdt.us-es.sel.de> kanze@us-es.sel.de
> (James Kanze) writes:
> >
> >What is the difference between a mutable reference and a pointer?
>
> Conversely: What is the difference between a reference and
> a CONST pointer?

The two main differences I see :

- The way we use them ( ./nothing vs ->/*)

- The fact that a reference always points to a (once) valid memory localtion
    (a pointer can be NULL).

  Sarayan




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Tue, 31 May 1994 16:58:37 GMT
Raw View
In article <KANZE.94May31150520@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:
>
>What is the difference between a mutable reference and a pointer?

Conversely: What is the difference between a reference and
a CONST pointer?

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: guthrie@miu.edu
Date: Tue, 31 May 94 20:52:18 CDT
Raw View
> |> In article <CqIppE.FqD@ucc.su.OZ.AU>, <maxtal@physics.su.OZ.AU>
writes:
> |> > In article <2s5dur$4ut@news.iastate.edu> guthrie@miu.edu writes:
> |> > >3) What I would REALLY like is mutable references;
> |> >  You already have them:
> |> >  struct X {
> |> >   Y& y;
> |> >   X(Y& yy) : y(yy) {}
> |> >  };
>
> |> >  Y y1, y2;
> |> >  X x(y1);
> |> >  ...
> |> >  x.~X();
> |> >  new(&x) X(y2);
> |> >  ...
>
Note that this really works more like a pointer, i.e. it "contains"
a mutable reference, which could just as well have been implemented
using a pointer.
But, it doesn't have the properties of a reference, i.e. automatic
access of all the properties of the referenced object...
>
> What is the difference between a mutable reference and a pointer?
Automatic de-referencing;

              automatic
            dereferencing    mutable
            --------------  ---------
reference  :     yes          no
pointer    :     no           yes
mutable-
  reference:     yes          yes

They are orthogonal concepts, and somehow got tied together!?

Greg Guthrie




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 1 Jun 1994 12:51:20 GMT
Raw View
In article <2sh0se$5dn@arcturus.ciril.fr> galibero@mines.u-nancy.fr (Olivier Galibert aka Sarayan) writes:
>In article <Cqp31o.33D@ucc.su.OZ.AU>, maxtal@physics.su.OZ.AU (John Max Skaller)
>writes:
>> [...]
>>  int* pi = 0;
>>  int& ri = *pi;
>
>I really wonder what the standard says about this. I think it says
>NULL cannot be dereferenced, or that it has undefined behaviour, but
>it that a dereference ?

 Its a point of contention. But I dont think there
is much doubt really that dereferencing a pointer is
not an "operation" of any functional content. Its sugar and syntax.

 *pi; // does nothing

Dereferencing a pointer implies to me the same as copying a pointer:
the "address" had better be a valid address. (NULL is a valid address).
But it doesnt have to address an object of the type of the pointer:
until you use it to do some operation (call a member, add it to
something , etc)

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 1 Jun 1994 13:09:51 GMT
Raw View
In article <9415214.11427@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>
>>galibero@mines.u-nancy.fr (Olivier Galibert aka Sarayan) writes:
>>>> >
>>>> >What is the difference between a mutable reference and a pointer?
>>>>
>>>> Conversely: What is the difference between a reference and
>>>> a CONST pointer?
>>>
>>>The two main differences I see :
>>>
>>>- The way we use them ( ./nothing vs ->/*)
>>
>> That is not an intrinsic but cultural difference, cultures change.
>
>Are we talking about C++ here, or are we trying to talk about the general
>concepts of pointer/reference in a language-independant way?
>In C++, it is an intrinsic difference.  I don't believe that it's possible
>to talk about the differences between pointers and references in a
>language-independant way.

 What I meant was that "the way we use them" is a cultural
thing -- it differs between programming shops, between programmers,
between libraries (written by vendors).

 None of that is intrinsic.
>
>>>- The fact that a reference always points to a (once) valid memory localtion
>>>    (a pointer can be NULL).
>>
>> misconception.
>
>No, quite correct.
>
>> int* pi = 0;
>> int& ri = *pi;
>
>That is not legal C++.  You compiler is free to generate code which
>sends you rude email about your poor coding style ;-)

 What is "legal" C++ is a matter for the non-existant
Standard. Even if the ARM says you cant dereference a pointer
to a non-object, it is not necessarily correct. At this point,
even if the WP says it, its not necessarily correct.
(THe WP currently bans polymormorphism, use of temporaries
and a few other things (see 9.2/20)!)

 In my opinion -- and I may end up writing the rules
of the object model -- dereferencing a pointer does nothing
more than allow the compiler to load the address in the pointer
variable into a machine register -- so cant cause any sort of
violation of anything related to the object tro which the
pointer points or the reference refers. (The address
must be valid, but that is an issue for the memory model).

 This is actually essential -- binding a refernce
to uninitialised storage is required and equivalent to
address taking:

 extern int x;
 int * px = &x;   // fine, note x not initialised yet
 int & rx = x;    // fine, ""
 int & r2x = *px; // fine, ""
 int x = sin(1);


 int *ip = NULL;
 int& ir = *ip;   // IMHO this is just fine
 int *i2p = &ir;  // and useful

Its _convention_ that says references should refer to valid
objects and addresses. I do NOT wish to break programs for
which the object to which a reference refers becomes or is invalid
if the reference is not used. (I might consider that if
a diagnostic were required)

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 1 Jun 1994 01:34:35 GMT
Raw View
In article <2sftnl$cpf@arcturus.ciril.fr> galibero@mines.u-nancy.fr (Olivier Galibert aka Sarayan) writes:
>> >
>> >What is the difference between a mutable reference and a pointer?
>>
>> Conversely: What is the difference between a reference and
>> a CONST pointer?
>
>The two main differences I see :
>
>- The way we use them ( ./nothing vs ->/*)

 That is not an intrinsic but cultural difference, cultures change.
>
>- The fact that a reference always points to a (once) valid memory localtion
>    (a pointer can be NULL).

 misconception.

 int* pi = 0;
 int& ri = *pi;

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: anthony_armenta@taligent.com (Anthony Armenta)
Date: Wed, 1 Jun 1994 14:39:33 GMT
Raw View
> >- The fact that a reference always points to a (once) valid memory localtion
> >    (a pointer can be NULL).
>
>  misconception.
>
>  int* pi = 0;
>  int& ri = *pi;

The above comment is absolutely right.   It is a misconception to think
that a reference
always points to a legal object.  Consider the following where T is a
class:

void f (T& t) { cout << t; }

void g(T* s) { f (*s); }

main () {
 T* r=0;
 g(r);
}

main() should be able to call g and pass T pointer (r) even if it refers to
NULL.  g() has no
way to tell at compile time if s points to NULL or real memory but it must
allow the
call to f() with the object *s.

This is a runtime problem, perfectly valid code.

Anthony




Author: galibero@mines.u-nancy.fr (Olivier Galibert aka Sarayan)
Date: 1 Jun 1994 03:55:58 GMT
Raw View
In article <Cqp31o.33D@ucc.su.OZ.AU>, maxtal@physics.su.OZ.AU (John Max Skaller)
writes:
> [...]
>  int* pi = 0;
>  int& ri = *pi;

I really wonder what the standard says about this. I think it says
NULL cannot be dereferenced, or that it has undefined behaviour, but
it that a dereference ?

  Sarayan




Author: hf@informatik.uni-karlsruhe.de (Harald Fuchs)
Date: 01 Jun 1994 14:59:04 GMT
Raw View
In article <CqpyDL.D38@ucc.su.OZ.AU>, maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>  Its a point of contention. But I dont think there
> is much doubt really that dereferencing a pointer is
> not an "operation" of any functional content. Its sugar and syntax.

>  *pi; // does nothing

What if "pi" is an instance of a class which has an operator* with side
effects?  I think in this case "*pi" should do something.
Unfortunately, this would be yet another difference between smart and
builtin pointers...
--
Harald Fuchs <hf@informatik.uni-karlsruhe.de>




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Wed, 1 Jun 1994 04:35:49 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>galibero@mines.u-nancy.fr (Olivier Galibert aka Sarayan) writes:
>>> >
>>> >What is the difference between a mutable reference and a pointer?
>>>
>>> Conversely: What is the difference between a reference and
>>> a CONST pointer?
>>
>>The two main differences I see :
>>
>>- The way we use them ( ./nothing vs ->/*)
>
> That is not an intrinsic but cultural difference, cultures change.

Are we talking about C++ here, or are we trying to talk about the general
concepts of pointer/reference in a language-independant way?
In C++, it is an intrinsic difference.  I don't believe that it's possible
to talk about the differences between pointers and references in a
language-independant way.

>>- The fact that a reference always points to a (once) valid memory localtion
>>    (a pointer can be NULL).
>
> misconception.

No, quite correct.

> int* pi = 0;
> int& ri = *pi;

That is not legal C++.  You compiler is free to generate code which
sends you rude email about your poor coding style ;-)

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Thu, 2 Jun 1994 13:36:25 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>galibero@mines.u-nancy.fr (Olivier Galibert aka Sarayan) writes:
>>maxtal@physics.su.OZ.AU (John Max Skaller)
>>writes:
>>> [...]
>>>  int* pi = 0;
>>>  int& ri = *pi;
>>
>>I really wonder what the standard says about this. I think it says
>>NULL cannot be dereferenced, or that it has undefined behaviour, but
>>it that a dereference ?
>
> Its a point of contention. But I dont think there
>is much doubt really that dereferencing a pointer is
>not an "operation" of any functional content.
>Its sugar and syntax.
>
> *pi; // does nothing

I think there's quite a bit of doubt.  You certainly haven't convinced
me yet! ;-)

Adopting this view-point would be quite a change from C.
In C the code

 *pi;

has undefined behaviour if `pi' does not point to an object
or if the object pointed to was uninitialized.

It would be possible to allow this in C++ as an extension to C,
but I don't think it is allowed by the ARM, and I remain dubious
as to the potential merits of any such extension.

[See also the recent discussion on comp.std.c regarding the semantics of

 volatile int *p = (int *)0x70000; // some memory-mapped I/O location
 *p; // has an important side-effect!
]

>Dereferencing a pointer implies to me the same as copying a pointer:
>the "address" had better be a valid address. (NULL is a valid address).
>But it doesnt have to address an object of the type of the pointer:
>until you use it to do some operation (call a member, add it to
>something , etc)

I think it might be useful to distinguish between expressions occuring
in "lvalue contexts" - expressions which are used to initialize
references, or whose address is taken, or which occur on the LHS of an
assignment operator - and expressions occurring in "rvalue contexts" -
those expressions whose value is fetched.  (I'm making that terminology
up as I go along ;-).  The former may evaluate to an lvalue designating
an uninitialized object, whereas the latter must not evaluate to an
uninitialized object.  The expression in an expression-statement is in
an "rvalue context".

So for example:

 int main() {
  int x;
  x;  // illegal C
    // also illegal C++, I suggest
  &x;  // legal C
    // also legal C++, I suggest
  int &y = x; // legal C++, I suggest
 }

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Thu, 2 Jun 1994 14:00:07 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>>> int* pi = 0;
>>> int& ri = *pi;
>>
>>That is not legal C++.  You compiler is free to generate code which
>>sends you rude email about your poor coding style ;-)
>
> What is "legal" C++ is a matter for the non-existant
>Standard.

Sure, but in the mean-time, we must go by the ARM, or the
latest working paper, or the behaviour specified by the
C standard, etc.

> This is actually essential -- binding a refernce
>to uninitialised storage is required and equivalent to
>address taking:

Sure, but binding a reference to a null pointer is a very
different thing.  It's not required, and furthermore allowing
it would potentially break lots of library routines which
assume that references do not refer to null pointers.

>Its _convention_ that says references should refer to valid
>objects and addresses.

Yes, convention -- and the ARM.  But even if it were only convention,
the convention is important, and we should not break it lightly, or
without good reason.

>I do NOT wish to break programs for
>which the object to which a reference refers becomes or is invalid
>if the reference is not used. (I might consider that if
>a diagnostic were required)

Such programs are already broken according to existing C++ language
rules.  I think honouring existing conventions is more important.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Thu, 2 Jun 1994 18:41:06 GMT
Raw View
In article <9415323.22632@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>>
>> Its a point of contention. But I dont think there
>>is much doubt really that dereferencing a pointer is
>>not an "operation" of any functional content.
>>Its sugar and syntax.
>>
>> *pi; // does nothing
>
>I think there's quite a bit of doubt.  You certainly haven't convinced
>me yet! ;-)
>
>Adopting this view-point would be quite a change from C.

 Of course, adopting references was quite a change from C too.
As was adopting classes. It cant be helped: C++ is not C.
>In C the code
>
> *pi;
>
>has undefined behaviour if `pi' does not point to an object
>or if the object pointed to was uninitialized.

 In C one cannot have:

 extern int i;
 int & j = i;
 int* pi = &i;
 j;
 *pi;

Both "j" and "*pi" are the same lvalue, and both refer to
the same uninitialised store, and both do nothing
except perhaps load the address of the store.
Neither requires an object to exist. Obviously because:

 j = 1;
 *pi = 1;

can be used to actually create the object.

 This is a distinct change from C -- in C
each use of an expression denoted a VALUE (as well
as possible an address if the expression were an lvalue).

 But in C++ this is not the case. Mere appearance
of a name does not denote a value -- C++ is object oriented
and there is no such thing as a value, at least for classes.
>
>It would be possible to allow this in C++ as an extension to C,
>but I don't think it is allowed by the ARM, and I remain dubious
>as to the potential merits of any such extension.

 The ARM cannot change the facts of reality.
It would be VERY DANGEROUS to make the behaviour of
a reference and a dereferenced pointer different.

 A reference MAY be bound to store not yet containing
an initialised object in C++ as far as I know. Use of
such a reference, whether it be a name declared as a reference
or a dereferenced pointer does NOT constitute access to the
storage or use of a value.

 Yes, that IS different to C. Cant be helped, it actually
is different. Now, the question is whether we should invent
any SPECIAL rules for the NULL pointer: it is by definition
a valid address even though it does not refer to any store.

 Quite simply I feel that if no attempt is made
to access the (non-existant) store to which the address
points then there is no error.

 Dereferencing a null pointer does not cause
a read or write to the non-existant store. So it should
be legal. In particular:

 char * p = NULL;
 char * q = &*p; // q == NULL

looks just fine to me: a smart compiler would optimise this to:

 char * q = p;

>
>[See also the recent discussion on comp.std.c regarding the semantics of
>
> volatile int *p = (int *)0x70000; // some memory-mapped I/O location
> *p; // has an important side-effect!
>]

 It would if an access to the memory were implied but none
is in C++. You cant say "the value is fetched and thrown away"
in C++ because C++ is object oriented and there ARE NO VALUES
to fetch: you CANT fetch the value of a class, only copy it
from one object to another. *p does not involve a copy, in C++
this operation is a nop. IMHO.

>
>I think it might be useful to distinguish between expressions occuring
>in "lvalue contexts" - expressions which are used to initialize
>references, or whose address is taken, or which occur on the LHS of an
>assignment operator - and expressions occurring in "rvalue contexts" -
>those expressions whose value is fetched.  (I'm making that terminology
>up as I go along ;-).  The former may evaluate to an lvalue designating
>an uninitialized object, whereas the latter must not evaluate to an
>uninitialized object.  The expression in an expression-statement is in
>an "rvalue context".

 Your terminology is fine and my answer is there are no
"rvalue contexts" in C++ at all. There is no choice or matter
of opinion here for classes -- perhaps there is a choice
for ints to follow the C model. My opinion is that C types should
follow the same model as classes for consistency, but I'd
be prepared to accept an argument that simplicity is less
important than compatibility.
>
>So for example:
>
> int main() {
>  int x;
>  x;  // illegal C
>    // also illegal C++, I suggest
>  &x;  // legal C
>    // also legal C++, I suggest
>  int &y = x; // legal C++, I suggest
> }

 Thats weird. "x;" is useless but I dont see why
its illegal :-)

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Thu, 2 Jun 1994 18:50:53 GMT
Raw View
In article <9415400.24217@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>
>> This is actually essential -- binding a refernce
>>to uninitialised storage is required and equivalent to
>>address taking:
>
>Sure, but binding a reference to a null pointer is a very
>different thing.  It's not required, and furthermore allowing
>it would potentially break lots of library routines which
>assume that references do not refer to null pointers.

 Not really. Those routines, just like others,
have pre-conditions. That a reference -- or pointer for that matter --
is not "null" is just a pre-condition.

 What it comes down to is this: banning null references
has no effect on diagnosis. But it renders some programs
that would otherwise be perfectly well formed ill formed
(gratuitously breaking sensible programs)
and there is no gain at all in adding a non-diagnosable constraint,
since every OTHER breach would be ill formed by some other rule.
(by specification)

 Therefore, null references should be permitted.

 Do you follow?
 Did I mess up the argument?
>
>Yes, convention -- and the ARM.  But even if it were only convention,
>the convention is important, and we should not break it lightly, or
>without good reason.

 Reason above: the convention has no advantages being
required by the Standard, but it does have disadvantages.
The situation would be different if a diagnostic were required.
--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: hartman@ulogic.UUCP (Richard M. Hartman)
Date: 2 Jun 94 23:54:04 GMT
Raw View
In article <Cqpz8F.Fwr@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>
> In my opinion -- and I may end up writing the rules
>of the object model -- dereferencing a pointer does nothing
>more than allow the compiler to load the address in the pointer
>variable into a machine register -- so cant cause any sort of
>violation of anything related to the object tro which the
>pointer points or the reference refers. (The address
>must be valid, but that is an issue for the memory model).

Yes, the address must be valid.  With protected memory, it is
possible that NULL might not represent a valid address, isn't it?

In addition, with memory mapped hardware (keyword 'volitile') the
act of loading the address into a register may have repercussions...

  -Richard Hartman
  hartman@ulogic.COM

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Just because you're paranoid doesn't mean they aren't out to get you....






Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Fri, 3 Jun 1994 13:41:43 GMT
Raw View
In article <3018@ulogic.UUCP> hartman@ulogic.COM writes:
>In article <Cqpz8F.Fwr@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>>
>> In my opinion -- and I may end up writing the rules
>>of the object model -- dereferencing a pointer does nothing
>>more than allow the compiler to load the address in the pointer
>>variable into a machine register -- so cant cause any sort of
>>violation of anything related to the object tro which the
>>pointer points or the reference refers. (The address
>>must be valid, but that is an issue for the memory model).
>
>Yes, the address must be valid.  With protected memory, it is
>possible that NULL might not represent a valid address, isn't it?

 No. NULL must be a valid "address". That is:

 void *a = NULL;
 void *b = a;

This copies the NULL pointer, and that had better not cause a hardware
fault. Its not unreasonable for an implementation to load NULL
into a machine register here, just as for *NULL. Similarly,
if the implementation CANT use a machine register for copying NULL,
it shouldnt load one dereferncing a pointer either.
>
>In addition, with memory mapped hardware (keyword 'volitile') the
>act of loading the address into a register may have repercussions...

 Thats implementation defined or unspecified.

 Tell me: given:

 void f(X&);
 X* x = NULL;
 f(*x);

is the access caused by '*x' a read or write access? What about:

 *x; // assume it were legal

You cant tell: its not in a context where the value the lvalue
represents is read nor where a store occurs. So it should neither
read nor write any memory. Including volatile memory.

 *x = 1;
 z = *x;

Here the context determines the usage, but even then ONLY
for int, char etc and NOT for user defined types X.
And here:

 X* x2 = &*x;

its clearly neither a read nor write: no memory access.
Sure, x has to be copied to x2, but that should work for NULL
as well. So I contend -- and I could be wrong -- that derferencing
by itself does nothing: its syntactic sugar with a meaning only
in a context -- just like any other lvalue.

In fact the C notation is misleading.

 *x = 1;

is really:

 x := 1;

where the LHS is required to be an address: this means
"store the value 1 at the address x". Thats how real
machines work and most low level assemblers.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Mon, 6 Jun 1994 05:30:53 GMT
Raw View
In article <2sh64t$qks@f111.iassf.easams.com.au> rjl@f111.iassf.easams.com.au (Rohan LENARD) writes:
>In article <2sh0se$5dn@arcturus.ciril.fr>,
>Olivier Galibert aka Sarayan <galibero@mines.u-nancy.fr> wrote:
>>In article <Cqp31o.33D@ucc.su.OZ.AU>, maxtal@physics.su.OZ.AU (John Max Skaller)
>>writes:
>>> [...]
>>>  int* pi = 0;
>>>  int& ri = *pi;
>>
>>I really wonder what the standard says about this. I think it says
>>NULL cannot be dereferenced, or that it has undefined behaviour, but
>>it that a dereference ?
>>
>>  Sarayan
>
>A language lawyer could say it is illegal since -
>
>ARM 8.4.3 References, (pg. 153)
>  "A variable declared to be a _T&_, this is "reference to type _T_"
>   (%8.2.2) must be initialized by an object of type _T_ or by an object
>   that can be converted into a _T_."
>
>and
>
>ARM 5.3 Unary Operators, (pg. 55)
>  "The unary _*_ operator means 'indirection': the expression must be a
>   pointer, and the result is an lvalue referring to the OBJECT to which
>   the expression points."
>
>Thus since the null pointer constant does not point at any object, the
>code is violating the constraint specified by 8.4.3 (that it must be an
>object). Hence the code is illegal.

As far as I know, there are no such things as ``constraints'', either in the
ARM or in the most recent ANSI/ISO C++ standardization draft.  Thus, your
assertion that the code is in some sense ``illegal'' may be unwarranted.

(Oh, and by the way, the ANSI/ISO C standard *does* contain very explicit
``constraints''.  I find it a pity that this simple concept hasn't been
given any formal status (yet) by x3j16/wg21.)

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- domain addr: rfg@netcom.com ----------- Purveyors of Compiler Test ----
---- uucp addr: ...!uunet!netcom!rfg ------- Suites and Bullet-Proof Shoes -




Author: guthrie@miu.edu
Date: Sat, 28 May 94 20:05:53 CDT
Raw View
In article <CqIppE.FqD@ucc.su.OZ.AU>, <maxtal@physics.su.OZ.AU> writes:
>
> In article <2s5dur$4ut@news.iastate.edu> guthrie@miu.edu writes:
> >
> >3) What I would REALLY like is mutable references;
>
>  You already have them:
>
>  struct X {
>   Y& y;
>   X(Y& yy) : y(yy) {}
>  };
>
>  Y y1, y2;
>  X x(y1);
>  ...
>  x.~X();
>  new(&x) X(y2);
>  ...
> --
>         JOHN (MAX) SKALLER,
John, This is very clever!
   However, I would rather have it builtin to the language;
The usage and syntax here obfuscate the underlying semantics, rather
like using call-by-pointer to simulate call-by-reference in C.
I thnk it is impressive that the underlying mechanisms are adequate to do
this.
Thanks,
   Greg Guthrie