Topic: Logical constness : A proposal
Author: jimad@microsoft.com (Jim Adcock)
Date: 06 Jul 92 19:11:30 GMT Raw View
In article <1992Jul2.025951.25184@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
|The current mechanism (casting away const) is flawed because it
|can be done outside member functions and because some people (including
|myself) think the use of casts for this purpose is itself error prone.
Also, it seems to break the concept of rvalues, and in particular
the meaning of "return by value."
These problem can be "fixed" by forcing compiler to introduce more
hidden temporaries, in particular to force them to always "return by copy"
but this seems [IMHO] to be a very high cost to pay for a dubious programming
hack.
Author: maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller)
Date: Wed, 8 Jul 1992 04:39:50 GMT Raw View
In article <1992Jul06.191130.28993@microsoft.com> jimad@microsoft.com (Jim Adcock) writes:
>In article <1992Jul2.025951.25184@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
>|The current mechanism (casting away const) is flawed because it
>|can be done outside member functions and because some people (including
>|myself) think the use of casts for this purpose is itself error prone.
>
>Also, it seems to break the concept of rvalues, and in particular
>the meaning of "return by value."
The concept of 'lvalue' and 'rvalue' is already broken
with user defined classes. What is an 'lvalue'? Is it something
to which an assignment can be done? Is it something that
can be converted to a non-const reference?
The concept of lvalue and rvalue makes sense for the standard
types because any change to an 'rvalue' cannot be used (since
there are no side effects). This is NOT the case for an arbitrarily
defined class, where one could assign to a temporary rvalue and
change the program state (via side effects) beyond the life
of the temporary.
Substring classes as a case in point.
Are these idioms a hack? Are the in fact flawed? Should they be used,
and if not, what mechanism is used instead?
At the moment my impression is that a non-const member function
MAY NOT be applied to an expression. That is because
1) If the return type is const value, it is converted to a const
reference (to which a non-const member function may not be applied)
2) If the return type is not const, then it cannot be converted
to a reference [Draft/ARM ??] at all, so NO member function
can be applied, including non-const ones.
If the return type is neither const nor non-const, the language
definition is incomplete because it does not specifically
state this fact, and does not resolve the issue of whether
member functions can be applied to temporaries (expressions)
--
;----------------------------------------------------------------------
JOHN (MAX) SKALLER, maxtal@extro.ucc.su.oz.au
Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------
Author: maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller)
Date: Tue, 30 Jun 1992 13:53:37 GMT Raw View
In article <4437@balrog.ctron.com> kbhatt@ctron.com (Kulbhushan Bhatt) writes:
>
[deleted details of proposal to distinguish between members of a
const object that might be changed and those that might not]
1) Is this like the proposal for ~const that ANSI/ISO has already considered?
[Support formation of comp.iso.c++ and then you amd I will avoid
reproposing things already proposed]
2) One problem is that the idea is not general enough. (This never
stopped C++ before mind you]
The problem is that you might have say:
class rational {int p,q; }
then even for a const object it makes sense to divide both p and q
by a common factor: this does not change the value.
The opposite problem is that a const object might contain a pointer
to something that can be modified, even for a const object.
Anyhow, a ~const member that can be changed without a cast even
in a const object sounds like a good idea.
--
;----------------------------------------------------------------------
JOHN (MAX) SKALLER, maxtal@extro.ucc.su.oz.au
Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------
Author: jss@lucid.com (Jerry Schwarz)
Date: Thu, 2 Jul 92 02:59:51 GMT Raw View
In article <1992Jul1.161843.10693@us-es.sel.de>, kanze@us-es.sel.de (James Kanze) writes:
|>
|> Note, however, that practically speaking, the definition of "const" in this
|> case ***is supplied by the programmer***, and not by the language!! I
|> suppose, however, that this is what is meant by abstract const.
|>
Exactly right. The meaning of "const" for C++ classes is determined
by the programmer. It has no fixed meaning. The situation is
analogous to assignment. T::operator= means whatever the
programmer says. Of course in both cases good style greatly
constrains what meanings are reasonable, but the language doesn't
enforce it.
There are some people who think that "const" ought to mean bitwise
constant for classes. I disagree because I think doing so requires
putting into the public interface a property that ought to be considered
an implementation detail.
|> Is this to be another case like that of overloaded operators, where any
|> semantic defined by the programmer is allowed (ie: operator+ does subtraction).
Yes.
|> If not, *how* can it be defined within the context of a programming language?
I don't think it can be. At least not in C++.
|> What one would like, of course, is something like the following identity:
|>
|> const BigFileType a (...) ;
|> BigFileType b (a) ;
|> // any legal operation on a
|> if (a == b) // should *always* be true.
|>
|> There are two problems in this:
|>
|> 1. There are no language imposed restrictions on the semantics of the
|> copy constructor and the assignment operator. In otherwords, there is
|> no guarentee that the above is true, even without any operation on a.
|>
|> 2 This only has meaning if the operator== is overloaded. I would be very
|> surprised if this were the case in the above example. (In this case, it
|> would mean comparing two highly structured disk files.)
|>
The problem was declaring "read" to be a const member. Although
it doesn't change the external file, a BigFileType (abstractly) also
contains a pointer into the external file and "read" does modify that.
|> Would one of the committee members please fill me in as to what *is* being
|> discussed! If anyone else has an opinion, I think this newsgroup would be
|> happy to hear it too.
Perhaps earlier postings of mine mislead you. You seem to understand
the situation. What is being discussed are alternative methods for
allowing the members of a class to modify the representation of a
class. It's generally agreed that arbitrary modifications to
the class in a const member function ought not to be allowed. What
is in dispute is how the programmer specifies that modification
is deliberate rather than accidental.
The current mechanism (casting away const) is flawed because it
can be done outside member functions and because some people (including
myself) think the use of casts for this purpose is itself error prone.
-- Jerry Schwarz
Author: jbuck@forney.berkeley.edu (Joe Buck)
Date: 2 Jul 1992 03:43:17 GMT Raw View
In article <1992Jul2.025951.25184@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
>Exactly right. The meaning of "const" for C++ classes is determined
>by the programmer. It has no fixed meaning. The situation is
>analogous to assignment. T::operator= means whatever the
>programmer says. Of course in both cases good style greatly
>constrains what meanings are reasonable, but the language doesn't
>enforce it.
If this interpretation prevails, we're all going to be sorry and our
code is going to be worse. Unfortunately it looks likely that it
will prevail. "const" has a particular meaning in ANSI C. To
deviate from that meaning in C++ should require stronger justification
than it has received so far.
>There are some people who think that "const" ought to mean bitwise
>constant for classes. I disagree because I think doing so requires
>putting into the public interface a property that ought to be considered
>an implementation detail.
It's perfectly possible to combine the ability of objects to cache
information or change their state in response to const member function
calls and the safety of enforcing "Alice const", as Jim Adcock calls it.
The way to do it is for the object to have a pointer to nonconstant
storage. This nonconstant storage is allocated in the constructor
and freed in the destructor. For example, we want to do
class Object {
...
public:
const Result& expensiveFunction() const;
}
but we want to cache the result of expensiveFunction so we only have
to compute it once. "All problems in computer science are solved by
another level of indirection".
class Object {
private:
Result* cache;
int* cacheValid;
...
public:
const Result& expensiveFunction() const {
if (!*cacheValid) {
computeAndCacheResult(cache);
*cacheValid = 1;
}
return *cache;
}
}
This is superior to cast-away-const for several reasons.
1. It permits stronger type checking. If I can assure that const is
never cast away in a program, and in the absence of major hackery to
defeat the type system, if a value is being changed in my very large
program I can assure that no const function is doing it. As soon as
you guys bless cast-away-const-whenever-a-programmer-feels-like-it,
everyone will be doing it and the compiler vendors will take all the
warnings out.
2. It permits better code to be generated, by aiding optimizing
compilers in eliminating checks for aliases.
If the extra indirection is unacceptable, perhaps the "~const"
proposal should be considered.
--
Joe Buck jbuck@ohm.berkeley.edu
Author: maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller)
Date: Wed, 1 Jul 1992 18:01:41 GMT Raw View
Re: abstract const.
This cannot be enforced by the language any more than a class
invariant can.
In other words: you could at best have a run-time test that, after
each operation compared the abstract value of an object with its
previous abstract value, checking whether abstract constness
had been violated.
So something like ~const is only an aid.
concrete const CAN be enforced on the static object, and indeed
it is at the moment. [and yes, you can modify the abstract value
without changing the concrete one by using a pointer.]
--
;----------------------------------------------------------------------
JOHN (MAX) SKALLER, maxtal@extro.ucc.su.oz.au
Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------
Author: maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller)
Date: Thu, 2 Jul 1992 17:55:03 GMT Raw View
In article <12tu0lINNbhv@agate.berkeley.edu> jbuck@forney.berkeley.edu (Joe Buck) writes:
>will prevail. "const" has a particular meaning in ANSI C. To
>deviate from that meaning in C++ should require stronger justification
>than it has received so far.
>
>It's perfectly possible to combine the ability of objects to cache
>information or change their state in response to const member function
>calls and the safety of enforcing "Alice const", as Jim Adcock calls it.
>The way to do it is for the object to have a pointer to nonconstant
>storage. This nonconstant storage is allocated in the constructor
>and freed in the destructor.
>
>This is superior to cast-away-const for several reasons.
>
>1. It permits stronger type checking. If I can assure that const is
>never cast away in a program, and in the absence of major hackery to
>defeat the type system, if a value is being changed in my very large
>program I can assure that no const function is doing it. As soon as
******
>you guys bless cast-away-const-whenever-a-programmer-feels-like-it,
>everyone will be doing it and the compiler vendors will take all the
>warnings out.
I have to take exception to *******.
If a class has a pointer to something and a const function
modifies what is pointed to, whether the abstract value of the object
is changed depends entirely on the purpose and meaning of the pointer.
For example: in a string class, the pointer would be to the
actual value, not just a cache.
For example: in a doubly linked list, one could easily
modify *this*.
>
>2. It permits better code to be generated, by aiding optimizing
>compilers in eliminating checks for aliases.
>
>If the extra indirection is unacceptable, perhaps the "~const"
>proposal should be considered.
>
>--
>Joe Buck jbuck@ohm.berkeley.edu
In general there is no solution, because one cannot define
the abstract value of an object so as to check whether it has been
modified (you can't even *trap* mods to const objects at run time,
let alone *prevent* such mods at compile time).
Therefore we must look to "common usage" to allow
the programmer to *help* the system.
In the bellow discussion, use of the syntax
X *p;
in a class is banned (temporarily). For each pointer you are
required to specify one option for its meaning. One of these
new syntaxes will then be made the default.
1) Full protection.
-------------------
This is an attempt to make the guarrantee Joe wants. Because
objects might contain pointers of the form
X *p;
these pointers are treated for const objects as
const X * const p;
so that calls of the form
p->nonconst();
are illegal. This makes such pointers act as if they were references.
It is a strengthening of the existing C++ rules.
const? X*p;
where 'const?' evaporates for non-const object, and becomes 'const'
for the const object.
2) Caches.
----------
To facilitate caches, the syntax
~const X p;
~const X *p;
~const X* ~const p;
is used. ~const means you can modify even for a const object.
Of course, const?, ~const and plain const can be used together.
3) Classes not free products.
------------------------------
If the values of a class correspond to all combinations of values
of its variables, the class is said to be the free product of
the variables.
In the case of a class invariant, some combinations are banned,
but we shall ignore this.
We can also have a class 'value invariant'. It is basically
the == operator. Consider the class
class Rational {int p,q;}
with the invariant "q!=0". It is still possible for two rationals
to be equal if their p and q parts do not correspond.
It makes sense to modify both p and q for a const object,
provided that the ratio p/q (mathematically) does not change,
that is, one might well have a "clean up" operation to
divide by common factors and ensure q is positive.
The same applies to normalisation of floating point numbers.
It is *usual* to do this every operation, not defer it to
a time when the object might be const, but not always.
Consider a string class in which, when the string is shortened,
the buffer storage is not reallocated. When you are short
of memory, you might want to realloc all the strings to their
actual sizes ... even the const ones.
In cases like this *cast-away-const* seems the best solution,
precisely because the classes might not be free products.
I would think a cast of the form:
(~const)p
[without mentioning the type] would be desirable.
Summary
-------
Modifying objects pointed to by pointer in const objects is a loophole
in the const mechanism, it is potentially dangerous, and should be
disallowed unless explicitly enabled.
Use of ~const in declarations will enable proper abstract const for objects
which are free products.
Abstract const for non-free product classes is best handled by
cast-away-const operator (~const).
Comments?
--
;----------------------------------------------------------------------
JOHN (MAX) SKALLER, maxtal@extro.ucc.su.oz.au
Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------