From: krc@wam.umd.edu (Kevin R. Coombes)
Subject: Re: Conceptual Constness
Keywords: const, const member, lazy
Organization: University of Maryland, College Park
Date: Tue, 29 Sep 1992 14:07:50 GMT

In article <BRENNAN.92Sep28124052@yosemite.hal.com> brennan@hal.com (Dave Brennan) writes:
>I write a lot of C++ classes that are optimized to not do anything any
>sooner than they have to.  Here's a contrived example:
>
>class Foo;
>
>class FooList
>{
>  public:
>  FooList ()
>    : foo_list (NULL) { }
>  // ...
>  foo operator [](int);
>  // ...
>
>  protected:
>  foo **foo_list;
>
>  void create_foo_list ();
>};
>
>foo &
>foo_list::operator [] (int index)
>{
>  if (foo_list == NULL)
>    create_foo_list ();
>
>  // [ error checking ommited ]
>
>  return (*foo_list[i]);
>}
>
>
>The list of Foo objects doesn't exist until a user of a FooList tries to
>access an element of the list.  Conceptually accessing an element is an
>operation that I would be like to be able to perform on a const object
>because conceptually it does not modify the FooList.
>
>My complaint is that C++ doesn't provide a way to indicate this conceputal
>"constness."  I really want to have a const FooList and be able to call a
>conceptually const function.  The current alternatives are either don't use
>const or do some ugly casting with "this" in conceptually const member
>functions.  The first solution makes it harder to insure conceptual const
>correctness, while the second is just plain ugly.
>
>Has this problem ever been addressed or pondered before by anyone else?
>Is there a better way around the problem?  Is this a problem worthy of a
><*gasp*> language extension?  (A dirty word around here, I know.  Almost
>as bad as "new keyword" :-)
>
>--
>Dave Brennan                                      HaL Computer Systems
>brennan@hal.com                                         (512) 794-2855
>
>Visit the Emacs Lisp Archive: archive.cis.ohio-state.edu:pub/gnu/emacs

Although I agree that the difference between "conceptual constness"
and "physical constness" is a real issue in C++, I cannot resist the
impulse to complain about some details of the admittedly contrived
example.

My first complaint is that
	operator[](int index)
should not be a member of a list class. Lists only provide sequential
access; Arrays provide random access.

Now, it is certainly fair to respond to my complaint by changing FooList
into FooArray. I am still not convinced that this particular example
raises the issue of "conceptual constness". If you just want to access
an element of a List or an Array, then should it not just be an error
if the list or array is empty? Why would you want to create an array at
this point?

You have said something about optimization, but I don't see how that applies
here. Perhaps you meant that you wanted to wait to create the collection
until someone tries to insert an element? But inserting an element in a
list is not a conceptually constant operation; the list changes when a
new item is inserted. (Note here that if a list or array is implemented
within the class as a pointer to some region of data, then inserting an
element is a physically const operation on the list pointer.)

In the example at hand, we can avoid the problem by modifying the
design

class FooArray {
 public:
  const Foo &operator[](int index) const;
  void insertAt(int index, Foo f);
};

I'm not about to claim that this is the ideal design. My point is that
fetching an element from an existing array is different from inserting
an element. One source of confusion is that the builtin operator[] can
be used for both operations. The tension in the design results from the
competing impulses: (i) to make operators on user-designed classes match
the builtin operators, and (ii) to enforce conceptual constness.

Finally, let me refer all interested parties to Coplien's book on
Advanced C++, where he provides a design sample which uses an intermediate
FooRef class to distinguish between access (rvalue) and insertion (lvalue)
uses of operator[].

Kevin Coombes <krc@math.umd.edu>
Newsgroups: comp.lang.c++