[comp.std.c++] const is definitely object-oriented

bmk@m2.csc.ti.com (Brian M Kennedy) (11/16/90)

I do not find any problem with the C++ definition of const.  It is fairly
simple and allows me the flexibility of defining what const means for new
types that I write.

For built-in types and structs/classes without user-defined constructors,
const means that the bit representation of the object doesn't change.  So,
a compiler can place such a const in ROM or compile it out of existence (ie.
hard-code it into immediates).

For new user-defined types, classes with user-defined constructors, const
means that the abstract object doesn't change.  The bit pattern may change.
Thus, the compiler cannot place such a const in ROM.  Furthermore, casting
away const-ness on such an object *is* well-defined and portable.

The compiler will enforce bitwise const-ness in const member functions of
user-defined types -- but that is just a programmer aid.  The programmer
simply responds "Yes, I know what I'm doing" by casting away the const-ness
of the this pointer.  Note that this is only valid for classes with user-
defined constructors or destructors.  Other classes are assumed to have
compiler-defined (i.e. bitwise) constness and may be in ROM.


MYTH: The "const" keyword in C++ specifies a property of storage, and
      as such is not particularly useful in an object-oriented program.

   The const keyword says nothing about storage for classes with user-
   defined constructors.  So, when you define a new object class, you
   have complete control over the meaning of const.  For built-in
   types, the bit representation *IS* the object.  So, saying a const int
   cannot change bit representation is just as object-oriented as saying
   a const int cannot change its abstract value.

MYTH: casting away const is non-portable

   This may be true for built-ins and classes without constructors,
   but is FALSE for classes with user-defined constructors.

MYTH: compilers are free to place const objects in ROM

   This may be true for built-ins and classes without constructors,
   but is FALSE for classes with user-defined constructors.

MYTH: cfront's behavior concerning const is the C++ definition of const

   cfront does *not* handle const correctly/understandably in 
   all cases.  Read Ellis & Stroustrup -- it describes const-ness
   rather clearly.

---------------------------------
Brian M. Kennedy <bmk@csc.ti.com>
Computer Systems Laboratory
Computer Science Center
Texas Instruments Incorporated

jimad@microsoft.UUCP (Jim ADCOCK) (11/27/90)

In article <1990Nov15.181026.24113@csc.ti.com> bmk@m2.csc.ti.com (Brian M Kennedy) writes:

|I do not find any problem with the C++ definition of const.  It is fairly
|simple and allows me the flexibility of defining what const means for new
|types that I write.

I disagree with all parts of all sentences of the above statement 1/2 :-)

|For built-in types and structs/classes without user-defined constructors,
|const means that the bit representation of the object doesn't change.  So,
|a compiler can place such a const in ROM or compile it out of existence (ie.
|hard-code it into immediates).

A compiler is clearly allowed to put such an object in ROM, but that does
not mean that the bit representation of such an object cannot change.

Some other conforming compiler can still allow cast-from-const for such objects.
Just because some compilers prohibit changing the bits of such objects
doesn't mean that all compilers must make such a prohibition.

|For new user-defined types, classes with user-defined constructors, const
|means that the abstract object doesn't change.  The bit pattern may change.
|Thus, the compiler cannot place such a const in ROM.  Furthermore, casting
|away const-ness on such an object *is* well-defined and portable.

I disagree.  I see nothing in Ellis & Stroustrup that clearly supports 
such a view.  Rather, I see Ellis & Stroustrup specify this as the
behavior one would typically expect on most current architectures,
rather than a constraint that all conforming compilers/architectures
must meet.  Contrary to your position, on the top of page 71 they
state that the effect of modifying via a cast from const is either to 
make the original object as-if originally declared non-const, 
or to cause a run-time exception.  This statement is made without 
restriction as to its applicability to objects with or without constructors, 
therefor I read this statement as being applicable to all objects, period.

|The compiler will enforce bitwise const-ness in const member functions of
|user-defined types -- but that is just a programmer aid.  The programmer
|simply responds "Yes, I know what I'm doing" by casting away the const-ness
|of the this pointer.  Note that this is only valid for classes with user-
|defined constructors or destructors.  Other classes are assumed to have
|compiler-defined (i.e. bitwise) constness and may be in ROM.

I disagree.  Compilers cannot in general, in the presence of separately
compiled and linked modules, strictly enforce bitwise constness in 
const member functions.  A programmer who casts away const is not
stating "Yes, I know what I'm doing" but rather is specifying 
"I'm willing to accept what my compiler[s] does to me in this situation."

|MYTH: casting away const is non-portable
|
|   This may be true for built-ins and classes without constructors,
|   but is FALSE for classes with user-defined constructors.

I disagree.  My copy of E&S on the top of page 109 says:

"Adding 'const' to a declaration ensures that an object to which the 
'const' is applied cannot have its value changed through an expression
involving the name being declared unless an explicit type conversion is
used to remove the 'constness' -- and possibly not even then."

Thus, I interpret Ellis & Stroustrup's wording as to say that casting
from const removes a program from the domain of strictly conforming
programs to that of at best a conforming program.  IE, that program may
run as intended by the programmer on some but not all compilers.

There are a multitude of reasonable things a programmer could want when
doing a cast-from-const, including:

*Ignore the fact that this object is constant and change it to some
new "constant."  Hopefully, said programmer changes from one "constant"
to some new "constant" infrequently!

*Ignore the fact that this non-const member funtion won't be able to change
the value of this read-only object -- I'm only trying to invoke the 
member function for some other side effects.

*Fail my executable with a memory fault if I ever try to actually molest
a constant -- I'm storing an address of a const in a non-const pointer, 
but I'm only using that value as an object ID -- I promise never to access
the constant object via the non-const pointer.

*Put this object into discardable memory -- Its okay to go back to previous
values if the present value of this object is discarded, and a prior
value is reloaded.  I want the speed of discardable memory, and I'm
willing to program in such a manner as to make it all work out right.

*I promise that the externally viewable attibutes of this objects won't
be changed by the "internal" changes I'm about to make.

*Inform me that I have made a programming mistake and tell me about my errors.

*Warn me that I may have made a programming mistake and list my possible errors.

*I'm calling code in an old library without any significant optimizations,
so that issues of constness verses non-constness can be safely ignored.

I do not see where Ellis & Stroustrup clearly prohibits many of the
above compiler implementation choices.  And I do see where E&S does
prohibit a few of the above *reasonable* choices.  So I don't see E&S
as being very clear on these matters.

|MYTH: compilers are free to place const objects in ROM
|
|   This may be true for built-ins and classes without constructors,
|   but is FALSE for classes with user-defined constructors.

I believe some compiler could be free to put an object with constructor
in read-only memory because Ellis & Stroustrup are less than crystal clear
on this matter.  They say that most practical architectures prevent 
switching memory from read-write to read-only after construction, therefor ...

They do not say that if such an architecture/compiler were to exist,
that that compiler could not so use the read-only memory.

In particular, on many systems it could be practical to evaluate at least
some global constructors of constant objects once, make a copy of that
memory to disk, then mark the entire region of global constants discardable.

|MYTH: cfront's behavior concerning const is the C++ definition of const
|
|   cfront does *not* handle const correctly/understandably in 
|   all cases.  Read Ellis & Stroustrup -- it describes const-ness
|   rather clearly.

I disagree that Ellis & Stroustrup describes the issue of const-ness clearly.
In particular, they do not clearly describe the rights and responsibilities
of programmmers verses compilers in this area, but rather only give one
description to cover both.  What is needed is an ANSI-C like division
of the problem into these two areas.

In any case, it seems like various people read E&S's descriptions of 
const in very different ways.  This would seem to be sufficient to say
that E&S is not clear in this manner.  I would hope that the ANSI 
committee would make the issue of const crystal clear, separating the
rights and responsibilities of compilers and programmers, what is
a strictly conforming program, verses mearly a conforming program.

[And these desires hold doubly true for volatile and const volatile too!]

A Humble Proposal:

Perhaps cast-from-const can be guaranteed to work as if the object was never
declared const iff the object is a non-parameter auto variable.  Any compiler
with function-level optimization should be able to handle this without
great difficulty -- the optimizations then become the same as if the
variable were never declared const.  In all other situations cast-from-const
would be implementation dependent.  --Asking more from compilers would seem to
have a major impact on optimization.