Transitive const sucks

Janice Caron caron800 at googlemail.com
Wed Sep 12 02:34:58 PDT 2007


On 9/12/07, Walter Bright <newshound1 at digitalmars.com> wrote:
>
> Janice Caron wrote:
> > So the function was declared const, because /conceptually/ it was.
> >
> > But the class had a mutable cache, declared with the C++ keyword
> "mutable"
> >
> > Transitivity would wreck that.
>
> You're right in that transitive const does not support the notion of
> "logical const" (which is the usual term for what you are referring to).
>
> The problem with logical const, however, is it offers no semantic
> guarantees. I pass a logical const reference around, and the underlying
> data may or may not change. I have no guarantees one way or the other,


But you could guarantee that /non-private/ data will not change. And since
non-private data is never seen outside the file in which it is declared, I
don't understand why this is a problem.


and even worse, I can't even tell this is happening. "I" here meaning
> the compiler, and our hapless code auditor.


Providing you restrict "logical constness" to private variables, is that
really true? Inside the module, the compiler knows everything. Outside the
module, the private variables are irrelevant anyway as they can never be
accessed in any way.


This just pulls the rug out from under:
>
> 1) functional programming


Functional programming isn't possible anyway unless the function can also
guarantee that it won't modify /global/ variables, and I see no way of
specifying that in the prototype.



2) multithreaded programming


Likewise, a function cannot be guaranteed to be completely threadsafe unless
it guarantees not to modify global variables, and I see no way of specifying
that in the prototype.

Even if you could, it's more complicated than that. A function might modify
global variables and yet still be threadsafe - providing it uses mutexes to
ensure it has exclusive access to the shared variables. A good example of
this is writef(). The writef() function writes to standard output, and so
/must/ modify some global state - but it's thread-safe because file access
is all mutex locked.

Moreover, multithreaded programming might /require/ you to lock a mutex, do
something, then unlock said mutex. The mutex itself needs to be modifiable -
that is, "logically const".

More thought needs to be put into that one.



3) having a tightly specified interface


Interfaces are my concern. If an Interface specifies that a function be
const (in the sense of non modifying member variables), then it would make a
lot of sense that classes which implement that interface be allowed to
assume the interface means "logical constness", for the reasons of all the
use-cases given so far.



It goes back to painting a stripe across your hips and calling it a
> seatbelt.
>
> Given this, it isn't any surprise that C++ is disastrously difficult to
> write functional & multithreaded programs in, and C++ mutability is one
> of the reasons why. In C++, you can write const this and const that and
> it doesn't mean jack squat. Many experienced C++ programmers will tell
> you that const is little more than a documentation aid.


I /am/ an experienced C++ programmer, and I agree. However, in C++, it is
possible to declare non-private members mutable - and to make matters worse,
the function bodies of functions which modify that variable could be in any
number of different source files scattered all over the place.

In D it's different. First off, I do suggest restricting "logical constness"
to private variables only. Secondly, all the function defintions which can
access those source files are in one file, so the compiler knows everything.


This means that, in D, when you declare a function as const, there would
still be an absolute guarantee that nothing non-private could ever be
changed by that function. Outside the module, that's all you need to know.
Inside the module, the compiler knows all anyway.



In other words, you're quite right that transitive const totally wrecks
> using const to specify logical constness. And that's a good thing <g>.
>

But logical constness is a /necessary/ thing, and if you outlaw any way of
doing it legitimately, then people will do it by "cheating". By (for
example), storing state in global variables. The simplest example I can come
up with is that random number one. Recall:

 class RandomNumberGenerator;
 {
     private long seed;

     const int rand() /* guarantees not to modify any non-private member
variables */
     {
         seed = f(seed);
         return (seed >> 32);
     }
 }

So then I'd have to change it to something like:

 private long seed;

 private long getSeed()
 {
     lockMutex();
     long s = seed;
     unlockMutex();
     return s;
 }

 class RandomNumberGenerator;
 {
     const int rand()
     {
         seed = f(getSeed());
         return (seed >> 32);
     }
 }


And I'd end up with something that was much nastier than the very thing you
are trying to avoid.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20070912/03a982c7/attachment.html>


More information about the Digitalmars-d mailing list