Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

delete of const objects o.k.?

16 views
Skip to first unread message

blargg

unread,
Aug 27, 1999, 3:00:00 AM8/27/99
to

In article <sa5aerd...@dune.gia.rwth-aachen.de>, Norbert Berzen
<nor...@dune.gia.rwth-aachen.de> wrote:

> Hi,
>
> sorry, if it's faq or if it has already been discussed.
> Is the following c++ formally o.k.?
>
> void f (void const * p)
> {
> delete p;
> }

How on Earth did you allocate a void?

void* p = new void; // sorry

It's undefined, first of all, regardless of const.

If it were some other type, like char const*, then it would be fine.
Lifetime is separate from const-ness. How else would you create a const
object and ever hope for it to be destroyed, if destroying/deleting a
const object were illegal?


[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]


Ron Natalie

unread,
Aug 28, 1999, 3:00:00 AM8/28/99
to
Norbert Berzen wrote:

> sorry, if it's faq or if it has already been discussed.
> Is the following c++ formally o.k.?
>
> void f (void const * p)
> {
> delete p;
> }

Const has no effect on construction or destruction.
The above is legal and the compiler is not obligated
to warn you.
---

Alexandre Oliva

unread,
Aug 28, 1999, 3:00:00 AM8/28/99
to
On Aug 27, 1999, Norbert Berzen <nor...@dune.gia.rwth-aachen.de> wrote:

> Is the following c++ formally o.k.?
> void f (void const * p) { delete p; }

Yes and no. You can delete const objects, yes, but deleting a void
object invokes undefined behavior, since you can't `new' a void.

> Neither egcs-1.1.2 nor tcc-4.1.2 complain

gcc 2.95+ does, AFAIR

--
Alexandre Oliva http://www.dcc.unicamp.br/~oliva IC-Unicamp, Bra[sz]il
oliva@{dcc.unicamp.br,guarana.{org,com}} aoliva@{acm.org,computer.org}
oliva@{gnu.org,kaffe.org,{egcs,sourceware}.cygnus.com,samba.org}
** I may forward mail about projects to mailing lists; please use them

Siemel B. Naran

unread,
Aug 28, 1999, 3:00:00 AM8/28/99
to
On 27 Aug 1999 19:57:15 GMT, blargg <postmast.ro...@iname.com> wrote:
>In article <sa5aerd...@dune.gia.rwth-aachen.de>, Norbert Berzen

>> Is the following c++ formally o.k.?


>>
>> void f (void const * p) { delete p; }

>How on Earth did you allocate a void?

You don't. You allocate a real object, then cast the T* to a void*.
As C supports "delete (void*)v", C++ supports it too.

void * v=new Derived(1,2,3);
delete v;

It should be illegal because you don't call the destructor.

--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------

Valentin Bonnard

unread,
Aug 28, 1999, 3:00:00 AM8/28/99
to
Siemel B. Naran wrote:

> You don't. You allocate a real object, then cast the T* to a void*.
> As C supports "delete (void*)v", C++ supports it too.

C was extended to support delete ?

(I think that's a step in right direction. Now,
add classes.)

--

Valentin Bonnard

James Kuyper Jr.

unread,
Aug 28, 1999, 3:00:00 AM8/28/99
to
"Siemel B. Naran" wrote:
....

> As C supports "delete (void*)v", C++ supports it too.

? When did they add 'delete' to standard C? They have free(), but that's
an entirely different matter, because the destructor isn't called.

On a related matter - Section 5.3.4 p3 says:
"...if the static type of the operand is different from its dynamic
type, the static type shall be a base class of the operand's dynamic
type ..."

Is that correctly worded? At this point, the operand either was a
pointer to an object, or has been converted to one from a class object.
As such, I don't see how it's dynamic type could be different from the
static type. That sentence would make more sense to me if 'operand' were
replaced with 'object', meaning the object pointed to by the pointer.

James Kuyper Jr.

unread,
Aug 29, 1999, 3:00:00 AM8/29/99
to

Valentin Bonnard wrote:
>
> Siemel B. Naran wrote:
>
> > You don't. You allocate a real object, then cast the T* to a void*.
> > As C supports "delete (void*)v", C++ supports it too.
>
> C was extended to support delete ?
>
> (I think that's a step in right direction. Now,
> add classes.)

At that point, why call it C?

Valentin Bonnard

unread,
Aug 30, 1999, 3:00:00 AM8/30/99
to
James Kuyper Jr. wrote:

> Valentin Bonnard wrote:

> > C was extended to support delete ?
> >
> > (I think that's a step in right direction. Now,
> > add classes.)
>
> At that point, why call it C?

'cause some people don't like the ``++'' part.

--

Valentin Bonnard
---

w...@fastdial.net

unread,
Aug 30, 1999, 3:00:00 AM8/30/99
to
In article <37C7E963...@wizard.net>,

"James Kuyper Jr." <kuy...@wizard.net> wrote:
> On a related matter - Section 5.3.4 p3 says:

Actually, it's 5.3.5p3, in case anyone was confused.

> "...if the static type of the operand is different from its dynamic
> type, the static type shall be a base class of the operand's dynamic
> type ..."
>
> Is that correctly worded? At this point, the operand either was a
> pointer to an object, or has been converted to one from a class
object.
> As such, I don't see how it's dynamic type could be different from the
> static type. That sentence would make more sense to me if 'operand'
were
> replaced with 'object', meaning the object pointed to by the pointer.

You are, of course, correct -- the last sentence of the paragraph,
which is obviously parallel to the one you cited, says "dynamic type
of the object to be deleted," which is what was intended here, too.

The Standard isn't as careful as it might be about applying the term
"dynamic type" to a pointer; cf 5.2.2p1, where the phrase "dynamic
type of an object expression" is defined as "the type of the object
pointed or referred to by the current value of the object expression,"
even though the object expression can be a pointer.

I don't think this is a particularly serious problem, though. Since
pointers themselves cannot have different static and dynamic types,
it's reasonably clear that the phrase "dynamic type of a pointer" is
just a shorthand for "dynamic type of the object to which a pointer
points," although technically incorrect.

--
William M. Miller, w...@fastdial.net
Software Emancipation Technology (www.setech.com)


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

Norbert Berzen

unread,
Aug 30, 1999, 3:00:00 AM8/30/99
to

Thanks for your answeres.
Here are three other questions, consider:

struct C {
~C() { mem = 4711; };
int mem;
};

void f (C const * cp)
{
delete cp; // C's destructor gets called.
// `void operator delete (void *) throw()' gets called.
}

Q1) Is a destructor function implicitely const qualified?

Q2) The parameter of `operator delete' is not const qualified.
Is that o.k.?

Q3) Is the above `delete cp;' equivalent to the following?

cp -> ~C();
operator delete (cp);


I know the code above is complete nonsense. I only think
there may be formal inconsistencies?

--
Norbert Berzen, Geodaetisches Institut der RWTH Aachen,
Templergraben 55, D-52062 Aachen
e-mail: nor...@dune.gia.rwth-aachen.de

Ron Natalie

unread,
Aug 30, 1999, 3:00:00 AM8/30/99
to

Siemel B. Naran wrote:

> >How on Earth did you allocate a void?
>

> You don't. You allocate a real object, then cast the T* to a void*.
> As C supports "delete (void*)v", C++ supports it too.
>

> void * v=new Derived(1,2,3);
> delete v;
>
> It should be illegal because you don't call the destructor.
>

Excuse me. C doesn't support "delete" anything. C++ doesn't
support delete (void*). The types must match the original or
be a base class of it or the behavior is undefined (it's got
nothing whatsoever to do with the destructor).

Jeff Rife

unread,
Aug 30, 1999, 3:00:00 AM8/30/99
to

Norbert Berzen (nor...@dune.gia.rwth-aachen.de) wrote:

> Here are three other questions, consider:
>
> struct C {
> ~C() { mem = 4711; };
> int mem;
> };
>
> void f (C const * cp)
> {
> delete cp; // C's destructor gets called.
> // `void operator delete (void *) throw()' gets called.
> }
>
> Q1) Is a destructor function implicitely const qualified?

No. Officially, your code never calls the destructor, so it doesn't matter
if it is const or not.

> Q2) The parameter of `operator delete' is not const qualified.
> Is that o.k.?

You are not calling "operator delete" in your code. You are using the
"delete operator".

There is a *huge* difference here, where the delete operator calls the
appropriate destructor (depending on polymorphism, etc.), and then calls
the appropriate operator delete (global or class-specific).

Then, operator delete usually just deallocates the memory. There could, of
course, be an overridden version that does other things, and some
implementations have a "debugging" version that does some extra checking.

> Q3) Is the above `delete cp;' equivalent to the following?
>
> cp -> ~C();
> operator delete (cp);

Functionally, yes.

However, if you actually try the above code, a conforming compiler
should emit a diagnostic, as ~C is not declared const, and you are
calling it with a const object.

This is one of those cases where the compiler is calling functions behind
the scenes, and those calls are not subject to const restrictions.

I also believe they are not subject to access restrictions.

--
Jeff Rife | GCS d+(-) s: a C++++(---)$ UL++(++++)$ P+ L+$
19445 Saint Johnsbury Lane | E--- W+++(--)$ N+ !o K? w++++(---)$ O- M--
Germantown, MD 20876-1610 | V-- PS+ PE(++) Y+ PGP t+ 5++ X+ R++(*) tv+
Home: 301-916-8131 | b+++ DI++++ D++ G+(++++) e+(*) h++(*) r* y?
Work: 301-770-5800 Ext 5335 |

David R Tribble

unread,
Aug 30, 1999, 3:00:00 AM8/30/99
to

Valentin Bonnard wrote:
>
> Siemel B. Naran wrote:
>
>> You don't. You allocate a real object, then cast the T* to a void*.
>> As C supports "delete (void*)v", C++ supports it too.
>
> C was extended to support delete ?

Uh, no. C supports 'free((void *)v);' as the only standard method
of deallocating dynamic (heap) memory. And the cast to 'void*' is
completely optional.

> (I think that's a step in right direction. Now, add classes.)

In the words of one of the ISO C committee members, there would be
several dead bodies over which this would be done.

The requirements of supporting member functions (i.e., name mangling)
is enough alone to complicate the linkage model of C that too many
people would say no, leave it alone, keep it simple. Throw in far
more complicated things like dynamic static initialization,
destructors, templates, exceptions, and so on, and then... well,
what's the point?

Perhaps the only useful thing that could be added to C without
too much fuss would be namespaces, and even that complicates the
simple linkage model used by C.

If you want C++ you know where to get it. If you don't want all
of that, then you should be happy using C. (I use both, and like
them both.)

[Cross-posted to news:comp.std.c -drt]

-- David R. Tribble, da...@tribble.com --

David R Tribble

unread,
Aug 30, 1999, 3:00:00 AM8/30/99
to

Norbert Berzen wrote:
>
> Sorry, if it's faq or if it has already been discussed.

> Is the following c++ formally o.k.?
>
> void f (void const * p)
> {
> delete p;
> }
>
> Neither egcs-1.1.2 nor tcc-4.1.2 complain and I do not
> get any clue reading the c++-draft. Do I miss something?

Many pre-ISO compilers warned about deleting a const object.
ISO C++ allows it, and the compiler is not obliged to issue a
warning.

This has been the source of much discussion in the past (circa
May 1999, I think). Some feel that it's a convenience to allow
deleting a const object without requiring an explicit cast to
remove the constness, while others feel that this opens up a hole
in the type safety of the language by allowing a function that
does not own an object to delete it. The debate was never settled.

BTW, your example should have used 'const Type * p' instead of a
void pointer.

Ron Natalie

unread,
Aug 31, 1999, 3:00:00 AM8/31/99
to
Norbert Berzen wrote:

Perhaps I'm missing some gedanken experiment you are trying
to perform here, but:

>
> Q1) Is a destructor function implicitely const qualified?

Destructor's can't be declared const. Nor is it necessary
to do so, they may be invoked on const objects. Constness
doesn't apply inside the destructor.

>
> Q2) The parameter of `operator delete' is not const qualified.
> Is that o.k.?

Yes.

>
> Q3) Is the above `delete cp;' equivalent to the following?
>
> cp -> ~C();
> operator delete (cp);
>

No, because the CV qualification on cp doesn't apply to
the call of operator delete here.
---

Steve Clamage

unread,
Aug 31, 1999, 3:00:00 AM8/31/99
to

Jeff Rife <jrife...@nabs.net> writes:

>Norbert Berzen (nor...@dune.gia.rwth-aachen.de) wrote:

>> Here are three other questions, consider:
>>
>> struct C {
>> ~C() { mem = 4711; };
>> int mem;
>> };
>>
>> void f (C const * cp)
>> {
>> delete cp; // C's destructor gets called.
>> // `void operator delete (void *) throw()' gets called.
>> }
>>

>> Q1) Is a destructor function implicitely const qualified?

>No. Officially, your code never calls the destructor, so it doesn't matter


>if it is const or not.

The definition of a delete-expression is that the destructor is
invoked, followed by some version of operator delete. In what sense
is the destructor "officially" not called?


>> Q2) The parameter of `operator delete' is not const qualified.
>> Is that o.k.?

>You are not calling "operator delete" in your code. You are using the
>"delete operator".

>There is a *huge* difference here, where the delete operator calls the
>appropriate destructor (depending on polymorphism, etc.), and then calls
>the appropriate operator delete (global or class-specific).

But that doesn't answer the question. An operator delete doesn't
deal with objects; it never sees an object. It gets a pointer to
an untyped area of raw storage (which has ceased to be an object).
There is thus no need for an operator delete to take a parameter of
type pointer to const.


>> Q3) Is the above `delete cp;' equivalent to the following?
>>
>> cp -> ~C();
>> operator delete (cp);

>Functionally, yes.

Well, it depends. The delete-expression always invokes the
destructor, then calls some version of operator delete. If
class C has a member operator delete, the delete-expression
calls it, but the replacement code (as shown) won't.

>However, if you actually try the above code, a conforming compiler
>should emit a diagnostic, as ~C is not declared const, and you are
>calling it with a const object.

No diagnostic needs to be emitted, because you are explicitly
allowed to delete an object via a pointer to const. You are not
allowed to declare a destructor to be const (or volatile). The
(one and only) destructor for a class can be invoked on any
object, whether or not it is const.

>This is one of those cases where the compiler is calling functions behind
>the scenes, and those calls are not subject to const restrictions.

No, it's because there is a special rule for this case.

And even when a function is implicitly called, all the access
rules apply. For example, this code should not compile:

class C {
~C(); // private
};
void f()
{
C c; // error, C::~C() not accessible
}

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

Valentin Bonnard

unread,
Aug 31, 1999, 3:00:00 AM8/31/99
to

Norbert Berzen wrote:
>
> Thanks for your answeres.

> Here are three other questions, consider:
>
> struct C {
> ~C() { mem = 4711; };
> int mem;
> };
>
> void f (C const * cp)
> {
> delete cp; // C's destructor gets called.
> // `void operator delete (void *) throw()' gets called.
> }
>
> Q1) Is a destructor function implicitely const qualified?

yes, although you can't write const on a dtor:

struct T {
~T () const; // error
};

> Q3) Is the above `delete cp;' equivalent to the following?
>
> cp -> ~C();
> operator delete (cp);

Assuming no exceptions, with no member operator delete, yes

> I know the code above is complete nonsense.

To me it's very sensible and useful code.

--

Valentin Bonnard

Valentin Bonnard

unread,
Sep 1, 1999, 3:00:00 AM9/1/99
to
David R Tribble wrote:

> Valentin Bonnard wrote:

> > C was extended to support delete ?

> Uh, no.

Thanks for the info.

> Perhaps the only useful thing that could be added to C without
> too much fuss would be namespaces,

??? (you must be jocking)

--

Valentin Bonnard
---

Jeff Rife

unread,
Sep 1, 1999, 3:00:00 AM9/1/99
to
Steve Clamage (cla...@eng.sun.com) wrote:

> >No. Officially, your code never calls the destructor, so it doesn't matter
> >if it is const or not.
>
> The definition of a delete-expression is that the destructor is
> invoked, followed by some version of operator delete. In what sense
> is the destructor "officially" not called?

By "officially", I meant that he never explicitly did:

cp->~C();

I know what the compiler does for the delete-expression, and said so,
although I did use the term "delete operator", instead.

> But that doesn't answer the question. An operator delete doesn't
> deal with objects; it never sees an object. It gets a pointer to
> an untyped area of raw storage (which has ceased to be an object).
> There is thus no need for an operator delete to take a parameter of
> type pointer to const.

Well, I *thought* I answered it, saying that what he did was OK, and should
work on all conforming implementations, but I guess I got the reason wrong.

> >This is one of those cases where the compiler is calling functions behind
> >the scenes, and those calls are not subject to const restrictions.
>
> No, it's because there is a special rule for this case.

And, this is where I got the reason wrong. Sorry I missed it, as it is in
a completely different section of the standard from the delete stuff, and
sometimes finding things is tough.

--
Jeff Rife |
19445 Saint Johnsbury Lane | http://www.nabs.net/Cartoons/OverTheHedge/CDChristmasList.jpg
Germantown, MD 20876-1610 |
Home: 301-916-8131 |
Work: 301-770-5800 Ext 5335 |

Douglas A. Gwyn

unread,
Sep 3, 1999, 3:00:00 AM9/3/99
to
David R Tribble wrote:
> Valentin Bonnard wrote:
> > (I think that's a step in right direction. Now, add classes.)
> In the words of one of the ISO C committee members, there would be
> several dead bodies over which this would be done.

I don't know who said that, but it is misleading if not outright
wrong. WG14 did spend *considerable* committee time, in the early
phases of work toward C9x, to evaluate proposals for some support
for classes in C9x. The main question was how much of C++ would be
needed to make "C with classes" really useful to the C programmer.
Most of us drew the line short of virtual functions. (As it turned
out, not even C++ [as of the start of the C++ standards process]
was enough to really satisfy OO programmers, witness the addition of
exceptions, templates, and namespaces.) Eventually a slight
consensus developed for the view that C++ was going to be widely
enough available that programmers wanting the OO paradigm would be
better served by just using C++.

Nathan Myers

unread,
Sep 5, 1999, 3:00:00 AM9/5/99
to

blargg <postmast.ro...@iname.com> wrote:

><nor...@dune.gia.rwth-aachen.de> wrote:
>> Is the following c++ formally o.k.?
>>
>> void f (Foo const * p) // [s/void/Foo/ -ncm]
>> {
>> delete p;

>> }
>
>Lifetime is separate from const-ness. How else would you create a const
>object and ever hope for it to be destroyed, if destroying/deleting a
>const object were illegal?

Norbert has (again) called attention to a fundamental design
error in the language. This is indeed a frequently raised topic,
but is very interesting in that it never fails to elicit examples
of embarrassingly specious reasoning from people who are otherwise
mostly sober and clear-headed.

Blargg's question above falls into that category, confusing
"pointer-to-const" (which may of course point at any object)
with a notional pointer-to-actually-const-object, which is not
expressible in the type system.

More interesting than the original question is why this issue
drives so many people off the rails, and what it shares with
other issues with the same effect. Such issues arising in
language design are very dangerous, and need to be identified
early.

--
Nathan Myers
n...@nospam.cantrip.org http://www.cantrip.org/

blargg

unread,
Sep 6, 1999, 3:00:00 AM9/6/99
to

In article <7qt8d2$ssm$1...@shell7.ba.best.com>, n...@nospam.cantrip.org
(Nathan Myers) wrote:

> blargg <postmast.ro...@iname.com> wrote:
> ><nor...@dune.gia.rwth-aachen.de> wrote:
> >> Is the following c++ formally o.k.?
> >>
> >> void f (Foo const * p) // [s/void/Foo/ -ncm]
> >> {
> >> delete p;
> >> }
> >
> >Lifetime is separate from const-ness. How else would you create a const
> >object and ever hope for it to be destroyed, if destroying/deleting a
> >const object were illegal?
>
> Norbert has (again) called attention to a fundamental design
> error in the language. This is indeed a frequently raised topic,
> but is very interesting in that it never fails to elicit examples
> of embarrassingly specious reasoning from people who are otherwise
> mostly sober and clear-headed.
>
> Blargg's question above falls into that category, confusing
> "pointer-to-const" (which may of course point at any object)
> with a notional pointer-to-actually-const-object, which is not
> expressible in the type system.

I am quite aware of the differences between these two guarantees. One can
be bound to non-const objects, as const currently means, while the other
can *only* be bound to const objects (similar to the recent discussion on
a pointer to final type, meaning it can only point to an object of that
complete type). I just don't see object lifetime as being connected to
const-ness (though I'm wide open to have my view changed with convincing
arguments).

My quoted statement above assumes no casting. Obviously one can cast to a
non-const pointer type prior to deletion.

> More interesting than the original question is why this issue
> drives so many people off the rails, and what it shares with
> other issues with the same effect. Such issues arising in
> language design are very dangerous, and need to be identified
> early.

Can you elaborate a bit?

(completely open to having all my reasoning on this matter shown to be
totally braindead)

Siemel B. Naran

unread,
Sep 6, 1999, 3:00:00 AM9/6/99
to
On 5 Sep 1999 14:56:53 GMT, Nathan Myers <n...@nospam.cantrip.org> wrote:
>blargg <postmast.ro...@iname.com> wrote:
>>nor...@dune.gia.rwth-aachen.de> wrote:

>>> Is the following c++ formally o.k.?
>>>
>>> void f (Foo const * p) // [s/void/Foo/ -ncm]
>>> {
>>> delete p;
>>> }

>>Lifetime is separate from const-ness. How else would you create a const
>>object and ever hope for it to be destroyed, if destroying/deleting a
>>const object were illegal?

>More interesting than the original question is why this issue


>drives so many people off the rails, and what it shares with
>other issues with the same effect. Such issues arising in
>language design are very dangerous, and need to be identified
>early.

It drives me crazy because I sometimes want an array of pointers
to existing objects, and I don't want the user to be able to
call delete on these pointers. For example, suppose that I have
a list of House objects, and I want a list of all houses that
cost $250,000 or more. Then I write this function

std::deque<const House *> expensive_houses(const std::list<Houses>&);

I'd prefer it if clients were not able to write

std::deque<const House *> e(expensive_houses(all_houses));
delete e.front();

Requiring the use of "const_cast<>" to delete a pointer to const
would be an easy addition to C++. It may be a little more
cumbersome for people to deal with -- ie, those people who really
must delete pointer to const objects. It may be somewhat
logically inconsistent, as Blargg points out above. But it would
make C++ just a little safer, and so it is a good thing.

---
---

But here's something else to consider. After I find all houses that
are $250,000 or more, I sometimes want to modify them some way. Then
my function becomes

std::deque<House *> expensive_houses(std::list<Houses>&);

And I still don't want users to be able to delete houses in the
std::deque. Only someone with the original list of houses should
be able to delete House objects.

So now you should not be able to apply delete to a pointer to any
object!


I have some ideas for enforcing my ideas, but I think they should be
the subject of third-party compiler tools rather than of the language.
Only a scope S that new's a object should be able to delete the
object. For example,
A::A() { a=new aa; }
A::~A() { delete a; }

But then, how do we deal with create functions? Here the create
function (scope S2) creates an object, and the function that calls the
create function (scope S1) deletes the object. I guess that if the
create function returns a std::auto_ptr<>, then the problem goes away.

--
--------------
siemel b naran
--------------
---

blargg

unread,
Sep 6, 1999, 3:00:00 AM9/6/99
to

In article <slrn7t5lq0....@localhost.localdomain>,
sbn...@uiuc.edu wrote:

> On 5 Sep 1999 14:56:53 GMT, Nathan Myers <n...@nospam.cantrip.org> wrote:
> >blargg <postmast.ro...@iname.com> wrote:
> >>nor...@dune.gia.rwth-aachen.de> wrote:
>
> >>> Is the following c++ formally o.k.?
> >>>
> >>> void f (Foo const * p) // [s/void/Foo/ -ncm]
> >>> {
> >>> delete p;
> >>> }
>
> >>Lifetime is separate from const-ness. How else would you create a const
> >>object and ever hope for it to be destroyed, if destroying/deleting a
> >>const object were illegal?
>
> >More interesting than the original question is why this issue
> >drives so many people off the rails, and what it shares with
> >other issues with the same effect. Such issues arising in
> >language design are very dangerous, and need to be identified
> >early.
>
> It drives me crazy because I sometimes want an array of pointers
> to existing objects, and I don't want the user to be able to
> call delete on these pointers. For example, suppose that I have
> a list of House objects, and I want a list of all houses that
> cost $250,000 or more.

Well, what if you didn't want users calling function "f" on these objects?
How would you express this in the language? Maybe this too is a situation
that shouldn't be directly represented in the language. How useful a
restriction is it, in light of the myriad of other restrictions you may
also want to enforce, but can't (and for some reason aren't driven crazy
by)?

> Then I write this function
>
> std::deque<const House *> expensive_houses(const std::list<Houses>&);
>
> I'd prefer it if clients were not able to write
>
> std::deque<const House *> e(expensive_houses(all_houses));
> delete e.front();
>
> Requiring the use of "const_cast<>" to delete a pointer to const
> would be an easy addition to C++.

So, in other words, you're slipping in this extra restriction just so you
don't have to add a proper restriction, perhaps "undeletable"? I don't
like having multiple semantics crammed together like this. What if you
want to express "undeletable" and "non-const"? Seems reasonable enough to
me, perhaps even in your example.

> It may be a little more
> cumbersome for people to deal with -- ie, those people who really
> must delete pointer to const objects. It may be somewhat
> logically inconsistent, as Blargg points out above. But it would
> make C++ just a little safer, and so it is a good thing.

I don't buy this. It seems to be in the same class as adding a laundry
list of features without any look at the overall picture.

> But here's something else to consider. After I find all houses that
> are $250,000 or more, I sometimes want to modify them some way. Then
> my function becomes
>
> std::deque<House *> expensive_houses(std::list<Houses>&);
>
> And I still don't want users to be able to delete houses in the
> std::deque. Only someone with the original list of houses should
> be able to delete House objects.

Ahhhh... told you so :-)

> So now you should not be able to apply delete to a pointer to any
> object!
>
> I have some ideas for enforcing my ideas, but I think they should be
> the subject of third-party compiler tools rather than of the language.

While these can be useful, I personally don't like having to use extra
tools. Integration with the development environment is a big concern to
me. I don't like tedious repetitive tasks (those are for the computer to
do).

> Only a scope S that new's a object should be able to delete the
> object. For example,
>
> A::A() { a=new aa; }
> A::~A() { delete a; }
>
> But then, how do we deal with create functions? Here the create
> function (scope S2) creates an object, and the function that calls the
> create function (scope S1) deletes the object. I guess that if the
> create function returns a std::auto_ptr<>, then the problem goes away.

This problem would be interesting to explore without constraining
ourselves to current C++.

Nathan Myers

unread,
Sep 7, 1999, 3:00:00 AM9/7/99
to

blargg <postmast.ro...@iname.com> wrote:

> n...@nospam.cantrip.org (Nathan Myers) wrote:
>> blargg <postmast.ro...@iname.com> wrote:
>> ><nor...@dune.gia.rwth-aachen.de> wrote:
>> >> Is the following c++ formally o.k.?
>> >> void f (Foo const * p) { delete p; }
>> >
>> >Lifetime is separate from const-ness. How else would you create a const
>> >object and ever hope for it to be destroyed, if destroying/deleting a
>> >const object were illegal?
>>
>> Norbert has (again) called attention to a fundamental design
>> error in the language. This is indeed a frequently raised topic,
>> but is very interesting in that it never fails to elicit examples
>> of embarrassingly specious reasoning from people who are otherwise
>> mostly sober and clear-headed.
>>
>> Blargg's question above falls into that category, confusing
>> "pointer-to-const" (which may of course point at any object)
>> with a notional pointer-to-actually-const-object, which is not
>> expressible in the type system.
>
> I am quite aware of the differences ...

> I just don't see object lifetime as being connected to
> const-ness (though I'm wide open to have my view changed with convincing
> arguments).

The mention of "object lifetime" has been a key source of confusion
among those posting on the subject. An object's lifetime is from
when the constructor returns until the destructor is entered.

Since the question is whether users should be allowed to apply the
delete-expression to a pointer-to-const, to mention a destructor
call, which must needs occur *after* the delete-expression, begs
the question. It is this confusion that allowed the mistake to be
made.

In other words, if constness and object lifetime are indeed unrelated,
then we should be discussing only the purpose of constness itself and
whether allowing the delete-expression is consistent that purpose.

> My quoted statement above assumes no casting. Obviously one can cast
> to a non-const pointer type prior to deletion.

Or one can retain a pointer-to-non-const for use in deletion.
Either is easily encapsulated by the owner of the object, and
need not be visible to clients.

>> More interesting than the original question is why this issue
>> drives so many people off the rails, and what it shares with
>> other issues with the same effect. Such issues arising in
>> language design are very dangerous, and need to be identified
>> early.
>

> Can you elaborate a bit?

I appreciate the open-mindedness; it would have been welcome when
the committee was in session. This has been gone over before in some
detail, but with the moderators' permission, I will summarize again.

Cfront never enforced constness in contexts like:

void f(T const* tp) { tp->T::~T(); }

for what are probably historical, and in any case formally indefensible
reasons; the destructor is certainly non-const. A const destructor
would be meaningless, and was detected as an error by Cfront.

It was noted that Cfront did not permit "delete tp" in that context.
Rather than restricting explicit destructor calls to non-const lvalues,
the laxity was extended to the delete-expression. The argument that
swayed most committee members appears to have been an argument by
analogy; the two cases

{ const T t; } // calls T::T(), followed by T::~T().
{ T const* tp = new T; delete tp; }

were seen by many as somehow equivalent. (At the time, "new T"
and "new const T" were semantically identical -- the "const" was
effectively ignored -- although this was changed with the addition
of some rather mystical language very late in the process, well
after the "fix".)

The argument was that since the compiler was obliged to call the
_destructor_ where the object goes out of scope, anybody should be
equally free to execute a _delete-expression_ there, or anywhere.

The practical argument for breaking const in this case was that
it had turned out to be "inconvenient" to cast away the pointer's
constness before destroying the object. That such "inconvenience"
is the whole point of const-correctness seemed lost on most of the
committee members at the time. (Also, the vote was taken, IIRC,
very late in the afternoon.)

After the fact, more sophisticated but equally specious arguments
have arisen to justify the change, bringing in details of template
argument deduction. All such arguments have been neatly disposed of.
(If anybody wants to try to resurrect any such arguments, I will be
happy to dispose of them again.) Still, none of the committee members
who argued for breaking const has conceded the point in public, despite
demolition of all their arguments; each has just dropped the subject.

The practical effect is that prior to the "fix", one could safely
pass or return a pointer-to-const to any function, and know that,
in the absence of a cast, nothing could be done to damage the object.
For example, one could place a sensitive resource in the object
pointed to, and know that it could not be stolen.

From a design standpoint, it should be clear to everyone that the
purpose of "const" on an interface is to allow the interface designer
to enforce invariants. Prior to the "fix", an interface could be
carefully designed to release a pointer-to-const, knowing that
nothing (without casting) could violate its invariants.

For example, one could write

T const* f() { T const* p = new T; g(p); return p; }

and, without knowing anything about g() or about f()'s caller (except
that they do not use casts), know that p was still usable and returnable.
Now, all bets are off. Const is broken, and the best we can hope for
is for coding standards and compilers to warn against deleting via a
pointer-to-const.

In practice, library designers, perforce, implement interfaces as if
the change had not happened, because to wrap every pointer up in a
class object just to enforce constness would be too messy. The effect
is that a common error that previously was easily detected at compile
time is no longer easily detectable at all.

That otherwise-careful committee members were swayed by such specious
arguments in this case -- as they were, also, in the case of delete-
expressions applied to pointers to incomplete types -- seems to me a
cause for deep concern. Perhaps in future language design discussions,
the "C++ Delete-Expression Fallacies" may be invoked when reasoning
threatens to go off the rails.

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Siemel B. Naran

unread,
Sep 7, 1999, 3:00:00 AM9/7/99
to

On 6 Sep 1999 20:53:57 GMT, blargg <postmast.ro...@iname.com> wrote:

>I don't buy this. It seems to be in the same class as adding a laundry
>list of features without any look at the overall picture.

Well the fact that you can delete a pointer-to-const is also a
feature, added without any look at the overall picture. In reality,
it should not be allowed because it violates const correctness.
The destructor may call a non-const function. And besides,
::operator delete or class::operator delete takes as its argument a
"void *", not a "void const *".

In your previous post you pointed out that because you can implicitly
delete a const object (ie, one created on the stack), you should be
able to delete a pointer to const object. But here's the difference:
for const objects, the object is deleted at the end of the block,
when it ceases to exist; for pointer-to-const object, the object is
deleted at the user's discretion. The rule that you can't delete
a pointer to const would add a small level of safety.

--
--------------
siemel b naran
--------------

blargg

unread,
Sep 7, 1999, 3:00:00 AM9/7/99
to

In article <slrn7t8n7o....@localhost.localdomain>,
sbn...@uiuc.edu wrote:

> On 6 Sep 1999 20:53:57 GMT, blargg <postmast.ro...@iname.com> wrote:
>
> >I don't buy this. It seems to be in the same class as adding a laundry
> >list of features without any look at the overall picture.
>
> Well the fact that you can delete a pointer-to-const is also a
> feature, added without any look at the overall picture.

Yes, I see this now.

"const" isn't some theoretical feature, it's a practical concept that has
non-smooth edges.

> In reality,
> it should not be allowed because it violates const correctness.
> The destructor may call a non-const function.

Good point! It's like an implicit cast to non-const.

> And besides,
> ::operator delete or class::operator delete takes as its argument a
> "void *", not a "void const *".

Yeah, but a const operation on an object may modify its bits (mutable). An
object is more than a bad-o-bits.

[snip comment about "equivalent" code example of local const T and new const T]

blargg

unread,
Sep 7, 1999, 3:00:00 AM9/7/99
to
In article <7r2m1t$n61$1...@shell7.ba.best.com>, n...@nospam.cantrip.org
(Nathan Myers) wrote:

> blargg <postmast.ro...@iname.com> wrote:
> > n...@nospam.cantrip.org (Nathan Myers) wrote:
> >> blargg <postmast.ro...@iname.com> wrote:
> >> ><nor...@dune.gia.rwth-aachen.de> wrote:
> >> >> Is the following c++ formally o.k.?
> >> >> void f (Foo const * p) { delete p; }
> >> >
> >> >Lifetime is separate from const-ness. How else would you create a const
> >> >object and ever hope for it to be destroyed, if destroying/deleting a
> >> >const object were illegal?
> >>
> >> Norbert has (again) called attention to a fundamental design
> >> error in the language. This is indeed a frequently raised topic,
> >> but is very interesting in that it never fails to elicit examples
> >> of embarrassingly specious reasoning from people who are otherwise
> >> mostly sober and clear-headed.
> >>
> >> Blargg's question above falls into that category, confusing
> >> "pointer-to-const" (which may of course point at any object)
> >> with a notional pointer-to-actually-const-object, which is not
> >> expressible in the type system.
> >
> > I am quite aware of the differences ...
> > I just don't see object lifetime as being connected to
> > const-ness (though I'm wide open to have my view changed with convincing
> > arguments).

[snip]


> In other words, if constness and object lifetime are indeed unrelated,
> then we should be discussing only the purpose of constness itself and
> whether allowing the delete-expression is consistent that purpose.

That does it :-)

There is a common dichotomy that I encounter when considering what
something should do. It is between theory and practice. This is something
that C++ has really taught me much about. So far, my arguments have been
theoretical mainly. They have dealt with concept clarity and
orthogonality.

> > My quoted statement above assumes no casting. Obviously one can cast
> > to a non-const pointer type prior to deletion.
>
> Or one can retain a pointer-to-non-const for use in deletion.
> Either is easily encapsulated by the owner of the object, and
> need not be visible to clients.

Right. new const T is really new T, then convert to const T*. new const T
should be meaningless. Just allocate with new T and use const T* to
enforce the const restriction.

> >> More interesting than the original question is why this issue
> >> drives so many people off the rails, and what it shares with
> >> other issues with the same effect. Such issues arising in
> >> language design are very dangerous, and need to be identified
> >> early.
> >
> > Can you elaborate a bit?
>
> I appreciate the open-mindedness; it would have been welcome when
> the committee was in session.

I consider this discussion here, and when I take the view that you are not
open-minded on the issue, I don't consider the discussion worthwhile. I
suppose being open-minded means that I can't determine whether you are
closed-minded until I can see your viewpoint first. I am seeing something
new now, and have been persuaded quite well - practicality wins :-)

[snip]


> The argument that
> swayed most committee members appears to have been an argument by
> analogy; the two cases
>
> { const T t; } // calls T::T(), followed by T::~T().
> { T const* tp = new T; delete tp; }
>
> were seen by many as somehow equivalent.

I came up with this "equivalence" too in considering the issue for myself,
so I am aware of its "lure".

> (At the time, "new T"
> and "new const T" were semantically identical -- the "const" was
> effectively ignored -- although this was changed with the addition
> of some rather mystical language very late in the process, well
> after the "fix".)

heh.

> The argument was that since the compiler was obliged to call the
> _destructor_ where the object goes out of scope, anybody should be
> equally free to execute a _delete-expression_ there, or anywhere.

Right. This is the theoretical argument, really. const isn't about theory,
though; it's about practical use (consider the addition of mutable, for
example).

> The practical argument for breaking const in this case was that
> it had turned out to be "inconvenient" to cast away the pointer's
> constness before destroying the object. That such "inconvenience"
> is the whole point of const-correctness seemed lost on most of the
> committee members at the time. (Also, the vote was taken, IIRC,
> very late in the afternoon.)

Yes. I still consider the "equivalence" example to be a little unsettling,
but const has other issues too (for example, binding a
pointer-to-non-const using "this" in a ctor, then modifying any member of
the object in a const member function - no cast!).

> After the fact, more sophisticated but equally specious arguments
> have arisen to justify the change, bringing in details of template
> argument deduction. All such arguments have been neatly disposed of.
> (If anybody wants to try to resurrect any such arguments, I will be
> happy to dispose of them again.)

What are the arguments? (feel free to e-mail them privately if you like :-)

Hmmm, could this have to do with a template allocating an object with "new
T", where T happens to be const? (hope I didn't open the can of worms)

> Still, none of the committee members
> who argued for breaking const has conceded the point in public, despite
> demolition of all their arguments; each has just dropped the subject.
>
> The practical effect is that prior to the "fix", one could safely
> pass or return a pointer-to-const to any function, and know that,
> in the absence of a cast, nothing could be done to damage the object.

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

> For example, one could place a sensitive resource in the object
> pointed to, and know that it could not be stolen.

And this does it for me. Weigh this against the "inconvenience" of the
"equivalent" code example, and it tips the scales very very easily.

Const was evolved for practical use in constraining what can be done to an
object. There is no "right" way, as I see it now, for it to interact with
object lifetime. It's whatever is most practical. This argument makes it
clear which choice is more practical. It stands on the fundamental use of
const - to show that something doesn't mess with soemthing else ("just
looking").

> From a design standpoint, it should be clear to everyone that the
> purpose of "const" on an interface is to allow the interface designer
> to enforce invariants. Prior to the "fix", an interface could be
> carefully designed to release a pointer-to-const, knowing that
> nothing (without casting) could violate its invariants.
>
> For example, one could write
>
> T const* f() { T const* p = new T; g(p); return p; }
>
> and, without knowing anything about g() or about f()'s caller (except
> that they do not use casts), know that p was still usable and returnable.
> Now, all bets are off. Const is broken, and the best we can hope for
> is for coding standards and compilers to warn against deleting via a
> pointer-to-const.

Thanks alot. Now I'm going to be wishing my compiler warned of deletion of
a pointer-to-const :-)

> In practice, library designers, perforce, implement interfaces as if
> the change had not happened, because to wrap every pointer up in a
> class object just to enforce constness would be too messy. The effect
> is that a common error that previously was easily detected at compile
> time is no longer easily detectable at all.
>
> That otherwise-careful committee members were swayed by such specious
> arguments in this case -- as they were, also, in the case of delete-
> expressions applied to pointers to incomplete types -- seems to me a
> cause for deep concern.

What about delete for incomplete types? Is that allowed too?!?

> Perhaps in future language design discussions,
> the "C++ Delete-Expression Fallacies" may be invoked when reasoning
> threatens to go off the rails.

heh. Thanks for putting me back on the rails.

Now, I have to ask "what else can I learn from this incident, in general,
about language reasoning?" I will be thinking about this for a while...
---

Steve Downey

unread,
Sep 7, 1999, 3:00:00 AM9/7/99
to

Siemel B. Naran <sbn...@uiuc.edu> wrote in message
news:slrn7t5lq0....@localhost.localdomain...


>
> It drives me crazy because I sometimes want an array of pointers
> to existing objects, and I don't want the user to be able to
> call delete on these pointers. For example, suppose that I have
> a list of House objects, and I want a list of all houses that

> cost $250,000 or more. Then I write this function


>
> std::deque<const House *> expensive_houses(const std::list<Houses>&);
>
> I'd prefer it if clients were not able to write
>
> std::deque<const House *> e(expensive_houses(all_houses));
> delete e.front();
>
> Requiring the use of "const_cast<>" to delete a pointer to const

> would be an easy addition to C++. It may be a little more


> cumbersome for people to deal with -- ie, those people who really
> must delete pointer to const objects. It may be somewhat
> logically inconsistent, as Blargg points out above. But it would
> make C++ just a little safer, and so it is a good thing.
>

> ---
> ---


>
> But here's something else to consider. After I find all houses that
> are $250,000 or more, I sometimes want to modify them some way. Then
> my function becomes
>
> std::deque<House *> expensive_houses(std::list<Houses>&);
>
> And I still don't want users to be able to delete houses in the

> std::deque. Only someone with the original list of houses should
> be able to delete House objects.


>
> So now you should not be able to apply delete to a pointer to any
> object!
>
>
> I have some ideas for enforcing my ideas, but I think they should be
> the subject of third-party compiler tools rather than of the language.

> Only a scope S that new's a object should be able to delete the
> object. For example,
> A::A() { a=new aa; }
> A::~A() { delete a; }
>
> But then, how do we deal with create functions? Here the create
> function (scope S2) creates an object, and the function that calls the
> create function (scope S1) deletes the object. I guess that if the
> create function returns a std::auto_ptr<>, then the problem goes away.
>

> --
> --------------
> siemel b naran
> --------------

> ---


I think the best way of handling something like this requirement, and the
second, is to write a wrapper class. IE:
namespace wrapper
{
class House
{
House * theHouse;
double price() {return theHouse->price();}
//...
}
}
std::deque<wrapper::House> expensive_houses(const std::list<impl::House>&) ;

If you want to modify some things about the House, define the interface that
allows the mods that you want, and add that to wrapper::House.

Users of the library never touch your houses

This communication is for informational purposes only. It is not intended as
an offer or solicitation for the purchase or sale of any financial instrument
or as an official confirmation of any transaction, unless specifically agreed
otherwise. All market prices, data and other information are not warranted as
to completeness or accuracy and is subject to change without notice. Any
comments or statements made herein do not necessarily reflect those of
J.P. Morgan & Co. Incorporated, its subsidiaries and affiliates.

Siemel B. Naran

unread,
Sep 8, 1999, 3:00:00 AM9/8/99
to
On 07 Sep 99 19:52:22 GMT, blargg <postmast.ro...@iname.com> wrote:
>In article <7r2m1t$n61$1...@shell7.ba.best.com>, n...@nospam.cantrip.org

>What are the arguments? (feel free to e-mail them privately if you like :-)

No, you should post them to the newsgroup.


>Const was evolved for practical use in constraining what can be done to an
>object. There is no "right" way, as I see it now, for it to interact with
>object lifetime. It's whatever is most practical. This argument makes it
>clear which choice is more practical. It stands on the fundamental use of
>const - to show that something doesn't mess with soemthing else ("just
>looking").

While I think that one should not be able to delete pointer-to-const
(instead, one should you const_cast then delete), I'm not sure if it
makes a big difference. Is anyone going to think of deleting the
pointer anyway?


>What about delete for incomplete types? Is that allowed too?!?

One cannot delete a pointer to an incomplete type because it is not
clear what destructor to call (the destructor may be virtual or
inline, but we don't know it).

One should not be able to delete a pointer to void for the same
reasons as above, but the standard allows it for backward
compatibility with C.

--
--------------
siemel b naran
--------------

Darin Adler

unread,
Sep 8, 1999, 3:00:00 AM9/8/99
to

Siemel B. Naran <sbn...@uiuc.edu> wrote:

> One should not be able to delete a pointer to void for the same
> reasons as above, but the standard allows it for backward
> compatibility with C.

The standard doesn't allow you to delete a pointer to void. 5.3.5 is the
section that explains the rules for how delete works, and a footnote in the
section highlights this very point: "This implies that an object cannot be
deleted using a pointer of type void* because there are no objects of type
void."

However, since 5.3.5 says the behavior is undefined, a compiler is allowed
to accept "delete x" where x is a void* and do anything and still be
conforming. A conforming compiler could issue an error message or a warning.
Another conforming compiler could successfully delete the object.

I'm not sure what you mean by backward compatibility with C, by the way,
since there's no "delete" in C.

-- Darin

Nathan Myers

unread,
Sep 8, 1999, 3:00:00 AM9/8/99
to

blargg <postmast.ro...@iname.com> wrote:
> n...@nospam.cantrip.org (Nathan Myers) wrote:
>> blargg <postmast.ro...@iname.com> wrote:
>> > I am quite aware of the differences [between a const object
>> > and the referent of a pointer-to-const] ...

>> > I just don't see object lifetime as being connected to
>> > const-ness (though I'm wide open to have my view changed with convincing
>> > arguments).
>[snip]
>> In other words, if constness and object lifetime are indeed unrelated,
>> then we should be discussing only the purpose of constness itself and
>> whether allowing the delete-expression is consistent that purpose.
>
>That does it :-)
>
>There is a common dichotomy that I encounter when considering what
>something should do. It is between theory and practice. This is something
>that C++ has really taught me much about. So far, my arguments have been
>theoretical mainly. They have dealt with concept clarity and
>orthogonality.
>
>> > My quoted statement above assumes no casting. Obviously one can cast
>> > to a non-const pointer type prior to deletion.
>>
>> Or one can retain a pointer-to-non-const for use in deletion.
>> Either is easily encapsulated by the owner of the object, and
>> need not be visible to clients.
>
>Right. new const T is really new T, then convert to const T*. new const T
>should be meaningless. Just allocate with new T and use const T* to
>enforce the const restriction.

Unfortunately, text has been added to muddle this point. It is now
possible, in principle, to create an actually-const object on the free
store -- even though there is only one operator new() (which returns
non-const void*) to allocate storage for it.

Since casting away const on a really-const object results in
undefined behavior, fixing const by restricting delete-expressions
requires also fixing this later change, or such an object could not
be deleted.

>What are the arguments? ...


>Hmmm, could this have to do with a template allocating an object with "new
>T", where T happens to be const? (hope I didn't open the can of worms)

Yes, that was Andrew Koenig's example:

template <typename T>
void f() {
T* tp = new T;
delete tp; // oops, what if T is "const int"?
}

demolished neatly by:

template <typename T>
void deleter(T const* tp) { delete const_cast<T*>(tp); }

template <typename T>
void f() {
T* tp = new T;
deleter(tp); // OK
}

(Of course this depends on the old equivalence of "new T" and
"new const T", which was broken shortly after const itself was
broken.)

>> Still, none of the committee members
>> who argued for breaking const has conceded the point in public, despite
>> demolition of all their arguments; each has just dropped the subject.
>>
>> The practical effect is that prior to the "fix", one could safely
>> pass or return a pointer-to-const to any function, and know that,
>> in the absence of a cast, nothing could be done to damage the object.
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>> For example, one could place a sensitive resource in the object
>> pointed to, and know that it could not be stolen.
>
>And this does it for me. Weigh this against the "inconvenience" of the
>"equivalent" code example, and it tips the scales very very easily.
>
>Const was evolved for practical use in constraining what can be done to an
>object. There is no "right" way, as I see it now, for it to interact with
>object lifetime. It's whatever is most practical. This argument makes it
>clear which choice is more practical. It stands on the fundamental use of

>const - to show that something doesn't mess with something else ("just


>looking").
>
>> From a design standpoint, it should be clear to everyone that the
>> purpose of "const" on an interface is to allow the interface designer
>> to enforce invariants. Prior to the "fix", an interface could be
>> carefully designed to release a pointer-to-const, knowing that
>> nothing (without casting) could violate its invariants.
>>
>> For example, one could write
>>
>> T const* f() { T const* p = new T; g(p); return p; }
>>
>> and, without knowing anything about g() or about f()'s caller (except
>> that they do not use casts), know that p was still usable and returnable.
>> Now, all bets are off. Const is broken, and the best we can hope for
>> is for coding standards and compilers to warn against deleting via a
>> pointer-to-const.
>
>Thanks alot. Now I'm going to be wishing my compiler warned of deletion of
>a pointer-to-const :-)

I believe that gcc-2.95 can be persuaded to warn about it.

>> In practice, library designers, perforce, implement interfaces as if
>> the change had not happened, because to wrap every pointer up in a
>> class object just to enforce constness would be too messy. The effect
>> is that a common error that previously was easily detected at compile
>> time is no longer easily detectable at all.
>>
>> That otherwise-careful committee members were swayed by such specious
>> arguments in this case -- as they were, also, in the case of delete-
>> expressions applied to pointers to incomplete types -- seems to me a
>> cause for deep concern.
>
>What about delete for incomplete types? Is that allowed too?!?

Believe it or don't, yes!

The reasoning here was that people should be able to global-change
"free(p)" to "delete p", and not allowing deletion of incomplete
pointer types would interfere with that. Fortunately, since it's
always wrong to do, every compiler can warn about it.

>> Perhaps in future language design discussions,
>> the "C++ Delete-Expression Fallacies" may be invoked when reasoning
>> threatens to go off the rails.
>
>heh. Thanks for putting me back on the rails.
>
>Now, I have to ask "what else can I learn from this incident, in general,
>about language reasoning?" I will be thinking about this for a while...

Maybe, don't conduct votes in the late afternoon?

(It was also a late-afternoon (and last-day) vote that resulted in the
std::char_traits<> members getting the wrong return values, so that they
must be implemented less efficiently than was previously possible.
Hint: std::memcpy() returns the wrong value; std::copy() returns the
right one.)

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Siemel B. Naran

unread,
Sep 8, 1999, 3:00:00 AM9/8/99
to

On 7 Sep 1999 21:51:21 GMT, Steve Downey <downey_...@jpmorgan.com> wrote:

>I think the best way of handling something like this requirement, and the
>second, is to write a wrapper class. IE:
>namespace wrapper
>{
>class House
>{
>House * theHouse;
>double price() {return theHouse->price();}
>//...
>}
>}
>std::deque<wrapper::House> expensive_houses(const std::list<impl::House>&) ;

Thanks for the idea. It's simple, yet I never thought of it. I don't
like the idea though because it means one new class in the project and
lots of writing forwarding functions. But I like your twist of making
a namespace for wrapper objects.

--
--------------
siemel b naran
--------------

blargg

unread,
Sep 9, 1999, 3:00:00 AM9/9/99
to

In article <FHr74...@research.att.com>, a...@research.att.com (Andrew
Koenig) wrote:

> In article <7r2m1t$n61$1...@shell7.ba.best.com>,


> Nathan Myers <n...@nospam.cantrip.org> wrote:
>
> >Still, none of the committee members
> >who argued for breaking const has conceded the point in public, despite
> >demolition of all their arguments; each has just dropped the subject.
>

> This statement is not true. I continue to believe that the
> decision to allow const objects to be deleted was the correct
> one, and I do not consider my arguments to have been demolished.

(this was the can of worms I was hinting at, Siemel, and why I suggested
that Nathan could perhaps e-mail me in private :-)

> The argument that I consider the strongest, and to which I have
> never seen a counterargument that is the least bit convincing,
> is that if you are allowed to create an object, you should be
> allowed to destroy it.
>
> That is, in
>
> template<class T> void f(args) {
>
> // ...
>
> }
>
> I should be able to say
>
> T* tp = new T;
> delete tp;
>
> There are other arguments, which other people may find more convincing,
> but I personally think that this one is the strongest.

template<typename T>
struct remove_const {
typedef T type;
};

template<typename T>
struct remove_const<T const> {
typedef T type;
};

typedef typename remove_const<T>::type T_non_const;

T* tp = new T_non_const;
delete const_cast<T_non_const*> (tp);

(or, as Nathan suggested, a deletion function that takes a const pointer
and casts it to non-const)

This one certainly shows an inconsistency in the concept, for sure.
Inconsistencies can pollute a simple concept, making it harder to
understand and apply consistently.

I'm not sure what the practical effect of the above is, though. Why would
T be a const type, and some function allocating and deallocating objects
of that type? What would it do with the objects once they were created? I
think this problem is more an issue that T could be a const type, and
interaction with templates. This points to a much bigger picture of
inconsistency. Making const consistent with this bigger inconsistency
isn't necessarily useful.

I've certainly run into many inconsistencies with templates (and even
simple typedefs) not related to deleting const objects.

> I understand that my point of view is not universally held, and
> I have no problem if other people hold other opinions. However,
> this argument has not been demolished, and if I have appeared to
> drop the subject, it is only because there is a limit to how many
> times I am willing to repeat myself in the absence of new evidence.

With the help of Nathan, I have called into question the meaning and use
of const.

I question whether it's a theoretically-pure concept or one of more
practical value. I am now leaning more towards the practical side. Bjarne
mentions that the concept came about from his experience with hardware
write-protection in operating systems (D&E page 90). There he describes
the concept evolving to suit practical needs (using it as a type
modifier).

As Nathan said, I think const was meant to provide a way to allow
something to access an object *without* messing with it. The class of the
object should decide exactly what "messes" with it. Member functions can
be declared "const" to allow this. The class should also be able to say
whether destruction is considered a modification or not. In the absence of
such a mecanism, it seems that prevent deletion of const is safer.

I suppose one could create two classes, and do away with const altogether:

class Foo_const {
protected:
void non_const();
~Foo_const();
public:
void const_f();
};

class Foo : public Foo_const {
public:
using Foo_const::non_const;
~Foo() { }
};

void f( Foo_const* foo ) {
foo->const_f(); // OK
foo->non_const(); // illegal
delete foo; // illegal
}

void g( Foo* foo ) {
foo->const_f(); // OK
foo->non_const(); // OK
delete foo; // OK
}

It seems that there is a lot of emotion involved in this issue, and many
"blown fuses", so I'm not really expecting that any reasonable discussion
will take place here on the matter.

As usual, everything is connected, and this issue flows into lots of other
issues in the language. I'd like to understand better the tradeoffs (and
mistakes) made, so that when I am desinging language-level mechanisms for
programs to use, I can incorporate experience of others into them.

Siemel B. Naran

unread,
Sep 9, 1999, 3:00:00 AM9/9/99
to

On 8 Sep 1999 16:18:24 GMT, Darin Adler <da...@bentspoon.com> wrote:
>Siemel B. Naran <sbn...@uiuc.edu> wrote:

>> One should not be able to delete a pointer to void for the same
>> reasons as above, but the standard allows it for backward
>> compatibility with C.

>I'm not sure what you mean by backward compatibility with C, by the way,


>since there's no "delete" in C.

Sorry. I should have set std::free(v). I think C allows it.

--
--------------
siemel b naran
--------------

James...@dresdner-bank.com

unread,
Sep 9, 1999, 3:00:00 AM9/9/99
to

In article <7r5als$9cl$1...@shell7.ba.best.com>,
n...@nospam.cantrip.org (Nathan Myers) wrote:

> Since casting away const on a really-const object results in undefined
> behavior, fixing const by restricting delete-expressions requires also
> fixing this later change, or such an object could not be deleted.

Is it casting away the const which is undefined, or actually trying to
change the object? The way I remember it (but they may have changed
things since I looked at the issue), you could cast away const anyway
you liked, but trying to modify an object was undefined behavior if (and
only if) the object itself was const and the type contained no mutable
members.

[...]


> >What are the arguments? ... Hmmm, could this have to do with a
> >template allocating an object with "new T", where T happens to be
> >const? (hope I didn't open the can of worms)

> Yes, that was Andrew Koenig's example:

> template <typename T>
> void f() {
> T* tp = new T;
> delete tp; // oops, what if T is "const int"?
> }

The problem with this argument (IMHO) is that it can be easily and
logically extended. What if the template is:

template< typename T >
void f()
{
T* tp = new T ;

*tp = 0 ; // oops, what if T is "const int"?
delete tp ;
}

The answer to both is the same: if the template modifies anything of
type T (and from a pratical point of view, deleting is effectively a
modification), then don't instantiate it on a const type.

(You can make a similar analogy by stating: what if T is int&. Yet no
one proposed being able to allocate and free references. I hope.)

[...]


> >What about delete for incomplete types? Is that allowed too?!?

> Believe it or don't, yes!

> The reasoning here was that people should be able to global-change
> "free(p)" to "delete p", and not allowing deletion of incomplete
> pointer types would interfere with that. Fortunately, since it's
> always wrong to do, every compiler can warn about it.

Which is, perhaps, the correct compromise. About the only time anyone
would make a global search and replace for free(p) is when converting C
code. And C code won't have any destructors, inheritance, etc., which
will make deleting an incomplete type a problem.

--
James Kanze mailto: James...@dresdner-bank.com
Conseils en informatique orientée objet/
Beratung in objekt orientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

Nathan Myers

unread,
Sep 9, 1999, 3:00:00 AM9/9/99
to
Andrew Koenig <a...@research.att.com> wrote:
>The argument that I consider the strongest, and to which I have
>never seen a counterargument that is the least bit convincing,
>is that if you are allowed to create an object, you should be
>allowed to destroy it.
>
>That is ... I should be able to say
>
> template<class T> void f() { T* tp = new T; delete tp; }
>
>... However,

>this argument has not been demolished, and if I have appeared to
>drop the subject, it is only because there is a limit to how many
>times I am willing to repeat myself in the absence of new evidence.

Andrew has indeed posted this example repeatedly.

I have not seen any reply from Andrew when it was pointed out that
before the "fix" that broke const it was trivial to write a template
function to delete the object:

template <class T> void deleter(T const* tp)
{ delete const_cast<T*>(tp); }

This allowed f() to be written:

template<class T> void f() { T* tp = new T; deleter(tp); }

Thus, it was already allowed to destroy any object one created.

Of course, after breaking const, the committee then broke the
equivalence of "new T" and "new const T", perhaps (it's not clear)
rendering the call to the deleter template above undefined.

"const" has always created inconveniences in coding, and some
very smart people have argued for making it a no-op. Still, it
is surprising to find Andrew arguing their side.

---

Siemel B. Naran

unread,
Sep 9, 1999, 3:00:00 AM9/9/99
to
On 8 Sep 1999 19:08:47 GMT, Andrew Koenig <a...@research.att.com> wrote:

>The argument that I consider the strongest, and to which I have
>never seen a counterargument that is the least bit convincing,
>is that if you are allowed to create an object, you should be
>allowed to destroy it.

My counter-argument is that you should not be able to create a
const argument. Instead, create a "new X" then cast the result
from "X *" to "X const *".

If you have to write a template function that creates a new
T object where T may be "X" or "X const", then use the
following helper class to remove top-level const.


template <class T>
struct no_toplevel_const { typedef T result; };

template <class T>
struct no_toplevel_const<const T> { typedef T result; };

--
--------------
siemel b naran
--------------

---

James Kuyper

unread,
Sep 9, 1999, 3:00:00 AM9/9/99
to
"Siemel B. Naran" wrote:
>
> On 8 Sep 1999 16:18:24 GMT, Darin Adler <da...@bentspoon.com> wrote:
> >Siemel B. Naran <sbn...@uiuc.edu> wrote:
...

> >I'm not sure what you mean by backward compatibility with C, by the way,
> >since there's no "delete" in C.
>
> Sorry. I should have set std::free(v). I think C allows it.

But there's no compatibility issues between C++ 'delete' and C free(),
so that can't be a reason. new/delete and malloc()/free() can't mix.
There might have been a desire to avoid unnecessary differences, to ease
understanding of 'delete', but that's doesn't make it a compatibility
issue.

Also, there's no such thing in C as std::free().

blargg

unread,
Sep 9, 1999, 3:00:00 AM9/9/99
to
In article <slrn7teb6m....@localhost.localdomain>,
sbn...@uiuc.edu wrote:

> On 8 Sep 1999 16:18:24 GMT, Darin Adler <da...@bentspoon.com> wrote:
> >Siemel B. Naran <sbn...@uiuc.edu> wrote:
>

> >> One should not be able to delete a pointer to void for the same
> >> reasons as above, but the standard allows it for backward
> >> compatibility with C.
> >

> >I'm not sure what you mean by backward compatibility with C, by the way,
> >since there's no "delete" in C.
>
> Sorry. I should have set std::free(v). I think C allows it.

Stop. Think. Then post. (I wouldn't have said this if it weren't for your
double-fault here)

C has no notion of construction and destruction. free() simply frees
memory allocated with malloc (or calloc or realloc). void* is used because
the memory can be used for anything. So yes, free() takes a void*.

Francis Glassborow

unread,
Sep 10, 1999, 3:00:00 AM9/10/99
to
In article <user-09099...@aus-as3-037.io.com>, blargg <postmast.
root.a...@iname.com> writes

>It seems that there is a lot of emotion involved in this issue, and many
>"blown fuses", so I'm not really expecting that any reasonable discussion
>will take place here on the matter.
>
>As usual, everything is connected, and this issue flows into lots of other
>issues in the language. I'd like to understand better the tradeoffs (and
>mistakes) made, so that when I am desinging language-level mechanisms for
>programs to use, I can incorporate experience of others into them.

Exploring this issue as instructive in language design is fine. However
remember that a degree of pragmatism was involved in the design of C++
(actually that is something that helped make it a popular language).
OTOH there is no way that the rule will change in so far as C++ is
concerned. Not now nor, IMO, ever. There is just too much code already
written to the present rules.


Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
---

Salters

unread,
Sep 10, 1999, 3:00:00 AM9/10/99
to

Nathan Myers wrote:
>
> Andrew Koenig <a...@research.att.com> wrote:
> >The argument that I consider the strongest, and to which I have
> >never seen a counterargument that is the least bit convincing,
> >is that if you are allowed to create an object, you should be
> >allowed to destroy it.
> >That is ... I should be able to say

> > template<class T> void f() { T* tp = new T; delete tp; }

> >... However,
> >this argument has not been demolished, and if I have appeared to
> >drop the subject, it is only because there is a limit to how many
> >times I am willing to repeat myself in the absence of new evidence.

> Andrew has indeed posted this example repeatedly.

> I have not seen any reply from Andrew when it was pointed out that

> before the "fix" that broke const it was trivial to write a template


> function to delete the object:

> template <class T> void deleter(T const* tp)
> { delete const_cast<T*>(tp); }

> This allowed f() to be written:

> template<class T> void f() { T* tp = new T; deleter(tp); }

> Thus, it was already allowed to destroy any object one created.

> Of course, after breaking const, the committee then broke the
> equivalence of "new T" and "new const T", perhaps (it's not clear)
> rendering the call to the deleter template above undefined.

> "const" has always created inconveniences in coding, and some
> very smart people have argued for making it a no-op. Still, it
> is surprising to find Andrew arguing their side.

> --
> Nathan Myers

I'm wondering about this "solution". Currently, delete can be used to
delete a const T (given a const T *). The proposal apparently is to
make this illegal, and then providing a loophole via deleter().

This would guarantee that a function would not call delete on a
pointer to const that is passed. E.g.

void f(const int* pi) {
delete pi;
}

would be illegal. How is this an advantage, when

void f(const int* pi) {
deleter(pi);
}

does the same, but is legal ?

I fail to see the advantage. This just changes the syntax required to
delete a pointer. The only way a client could find out (under the new
rules) if a pointer to const is deleted, is to look to the source of
the function. But this is exactly the problem the solution was intended
to solve. Therefore I'm inclined to conclude this is a non-solution.

I do find const sometimes annoying, especially in combination with the
STL, but this is not one of the areas where I curse it (away).

Michiel Salters

Ross Smith

unread,
Sep 10, 1999, 3:00:00 AM9/10/99
to
Andrew Koenig wrote:
>
> The argument that I consider the strongest, and to which I have
> never seen a counterargument that is the least bit convincing,
> is that if you are allowed to create an object, you should be
> allowed to destroy it.

For const objects, I would have thought it made more sense to fix the
asymmetry by disallowing creation than by allowing destruction. Does
anyone have any practical use for new(const foo)?

--
Ross Smith <ros...@ihug.co.nz> The Internet Group, Auckland, New Zealand
========================================================================
"There are many technical details that make Linux attractive to the
sort of people to whom technical details are attractive." -- Suck
---

Lisa Lippincott

unread,
Sep 10, 1999, 3:00:00 AM9/10/99
to
Andrew Koenig <a...@research.att.com> opined:

> The argument that I consider the strongest, and to which I have
> never seen a counterargument that is the least bit convincing,
> is that if you are allowed to create an object, you should be
> allowed to destroy it.

While I think that argument carries considerable practical force, this
abstract argument may be more convincing to some:

Consider the sequence of steps involved in creating an object.

First, storage is obtained;
second, subobjects are constructed;
third, the body of the constructor is executed;
finally, the lifetime of the object begins. If the object is const,
it may not be modified.

It's pretty hard to argue for a different arrangement. Does anyone want
to try?

Most people will also agree that the demolition of an object should
reverse the creation. Thus:

First, the lifetime of the object ends;
second, the body of the destructor is executed;
third, subobjects are destroyed;
finally, storage is released.

Looking at this chronology, it's clear that the destructor does not
operate on the object -- instead, it operates on the subobjects. The
question of whether the former object was const is moot.

So there is no sense in which destruction or deletion is a non-const
operation.

--Lisa Lippincott
---

Valentin Bonnard

unread,
Sep 11, 1999, 3:00:00 AM9/11/99
to

Ross Smith wrote:

> Does
> anyone have any practical use for new(const foo)?

Maybe to create a const instance. Does anybody have
any use of auto const Foo foo; // Foo is a UDT

--

Valentin Bonnard

Nathan Myers

unread,
Sep 12, 1999, 3:00:00 AM9/12/99
to

Salters <sal...@lucent.com> wrote:
>Nathan Myers wrote:
>> Andrew Koenig <a...@research.att.com> wrote:
>> > ... I should be able to say
>> > template<class T> void f() { T* tp = new T; delete tp; }
>>
>> ...before the "fix" that broke const it was trivial to write a template

>> function to delete the object:
>>
>> template <class T> void deleter(T const* tp)
>> { delete const_cast<T*>(tp); }
>>
>> Thus, it was already allowed to destroy any object one created.
>
>> "const" has always created inconveniences in coding, and some
>> very smart people have argued for making it a no-op. Still, it
>> is surprising to find Andrew arguing their side.
>
>I'm wondering about this "solution". Currently, delete can be used
>to delete a const T (given a const T *). The proposal apparently is to
>make this illegal, and then providing a loophole via deleter().

deleter<>() was not promoted as a "solution". It is a demonstration.
There is no proposal, especially for any kind of "loophole".

The discussion is about a committee decision which broke the previously
consistent semantics of const. deleter<>(), which has always been
possible, demonstrates that the decision failed to add any new
capability in the language.

This is unfortunate, as only a large corresponding improvement to the
language could have justified breaking const semantics. The reason
for discussing this ancient history here is that every time it comes
up, otherwise clear-headed people prove they are not thinking clearly
about the issue.

It distresses me to see the same few logically-unsound arguments being
posted time after time. What is it about this issue that causes people
to post obviously unsound arguments?

>I do find const sometimes annoying, especially in combination with the
>STL, but this is not one of the areas where I curse it (away).

Const is annoying because it's supposed to be annoying. You are not
being annoyed in this area because const has already been broken.

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Nathan Myers

unread,
Sep 12, 1999, 3:00:00 AM9/12/99
to

Lisa Lippincott <lisa_li...@advisories.com> wrote:
>Most people will also agree that the demolition of an object should
>reverse the creation. Thus:
>
> First, the lifetime of the object ends;
> second, the body of the destructor is executed;
> third, subobjects are destroyed;
> finally, storage is released.
>
>Looking at this chronology, it's clear that the destructor does not
>operate on the object -- instead, it operates on the subobjects. The
>question of whether the former object was const is moot.
>
>So there is no sense in which destruction or deletion is a non-const
>operation.

This assertion has been made repeatedly by apologists for the (now)
status quo. As in the case of most purely theoretical arguments, it
completely ignores the consequences of the alternatives. It is in the
consequences where we find the differences that affect programming.

Still, if you insist on the theoretical approach, then we can say that,
yes, once the destructor is entered the question of const or non-const
is moot. However, the question was of who should be allowed to cause
the destructor to be entered. Thus, Lippincott's argument purely begs
the question.

The decision to break const for the case of delete-expressions may turn
out to be a minor wart in the language. What is far worse, in my view,
is the breakdown in logical reasoning processes it seems to bring
repeatedly to the surface. Lippincott's attempt at argument by begging
the question was far from the first, and probably will not be the last,
that we have seen here.

What is it about this issue that leads people to advance arguments so
clearly stated, but logically bankrupt?

Dave Harris

unread,
Sep 12, 1999, 3:00:00 AM9/12/99
to
sal...@lucent.com (Salters) wrote:
> How is this an advantage, when
>
> void f(const int* pi) {
> deleter(pi);
> }
>
> does the same, but is legal ?

That code has an explicit type-cast, albeit hidden in the deleter
function. The ability to use a const_cast does not make const useless. The
idea is that you can break the rules, but you have to be explicit about
it. The code is safe by default and you can, for example, trivially search
for const_cast<>s to find possibly unsafe code.

Compare with the situation for normal methods:

template <typename T>
void caller( const T *p ) {
const_cast<T*>(p)->method();
}

class Object {
public:
void method();
//...
};

void f( const Object *pObj ) {
pObj->method(); // error; const object.
caller( pObj ); // OK.
const_cast<T*>(pObj)->method(); // Also Ok.
}

You can't call the non-const method directly on a const object, but you
can if you use a cast or a function that uses a cast. Same thing.

Deleter() is not a special magic; it was brought up only because the
explicit cast, as in the 3rd line of f() above, is not always convenient
when writing templates.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."
---

Nathan Myers

unread,
Sep 12, 1999, 3:00:00 AM9/12/99
to
Valentin Bonnard <Bonn...@wanadoo.fr> wrote:
>Ross Smith wrote:
>
>> Does
>> anyone have any practical use for new(const foo)?
>
>Maybe to create a const instance. Does anybody have
>any use of auto const Foo foo; // Foo is a UDT

You make a const auto object to bind const& arguments to.

The analogous process doesn't apply to new'd objects, because
new doesn't return the object, but a pointer to it:

{
const Foo foo;
f(foo); // template <class T> void f(T const&);
Foo const* fp = new Foo;
f(*fp);
}

This is not to say that a closer analogy could not be constructed;
rather, that argument by analogy is unlikely to lead anywhere
meaningful. Consider consequences, and you may make progress.

Ross Smith

unread,
Sep 12, 1999, 3:00:00 AM9/12/99
to

Andrew Koenig wrote:
>
> In article <37D8973E...@ihug.co.nz>,

> Ross Smith <ros...@ihug.co.nz> wrote:
>
> >For const objects, I would have thought it made more sense to fix the
> >asymmetry by disallowing creation than by allowing destruction. Does

> >anyone have any practical use for new(const foo)?
>
> Every restriction that applies to some types but not others
> makes it harder to write useful templates.
>
> The question is not whether new(const foo) is useful, but rather
> whether new T is useful, where T is a template parameter that
> happens to be bound to const foo.

Well, is it? Can you give an example -- I mean a realistic example where
it's actually useful, not just some toy code that uses it?

I don't see why the inability to use new is any more of a handicap than
the inability to call a non-const member function, or to use it on the
LHS of an assignment, or anything else that const objects can't do.

--
Ross Smith <ros...@ihug.co.nz> The Internet Group, Auckland, New Zealand
========================================================================
"There are many technical details that make Linux attractive to the
sort of people to whom technical details are attractive." -- Suck

Valentin Bonnard

unread,
Sep 13, 1999, 3:00:00 AM9/13/99
to
Nathan Myers wrote:
>
> Valentin Bonnard <Bonn...@wanadoo.fr> wrote:
> >Ross Smith wrote:
> >
> >> Does
> >> anyone have any practical use for new(const foo)?
> >
> >Maybe to create a const instance. Does anybody have
> >any use of auto const Foo foo; // Foo is a UDT
>
> You make a const auto object to bind const& arguments to.

???

You can do that with a non const object as well:

Foo foo;
const Foo& ref = foo;

The reasons for making auto objects of UDT const and
dynamic objects const are basically the same.

--

Valentin Bonnard
---

Francis Glassborow

unread,
Sep 13, 1999, 3:00:00 AM9/13/99
to
In article <memo.19990911...@btinternet.com>, Dave Harris
<sc...@btinternet.com> writes

>That code has an explicit type-cast, albeit hidden in the deleter
>function. The ability to use a const_cast does not make const useless. The
>idea is that you can break the rules, but you have to be explicit about
>it. The code is safe by default and you can, for example, trivially search
>for const_cast<>s to find possibly unsafe code.

However deleter() might well be shipped as object code in which case you
no longer have source to search. Personally I am very unhappy at any
use of const_cast<> that allows some process that is not known to be
safe. IOWs whenever you use const_cast<> it should be to remove a const
that has been acquired during some process of parameter passing though
the original object was not so protected. Removing const from an object
that was defined as such is always deeply suspect.

Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Ziv Caspi

unread,
Sep 13, 1999, 3:00:00 AM9/13/99
to
On 12 Sep 1999 00:07:51 GMT, n...@nospam.cantrip.org (Nathan Myers)
wrote:

>
>Lisa Lippincott <lisa_li...@advisories.com> wrote:
>>Most people will also agree that the demolition of an object should
>>reverse the creation. Thus:
>>
>> First, the lifetime of the object ends;
>> second, the body of the destructor is executed;
>> third, subobjects are destroyed;
>> finally, storage is released.
>>
>>Looking at this chronology, it's clear that the destructor does not
>>operate on the object -- instead, it operates on the subobjects. The
>>question of whether the former object was const is moot.
>>
>>So there is no sense in which destruction or deletion is a non-const
>>operation.

[...]


>Still, if you insist on the theoretical approach, then we can say that,
>yes, once the destructor is entered the question of const or non-const
>is moot. However, the question was of who should be allowed to cause
>the destructor to be entered. Thus, Lippincott's argument purely begs
>the question.

It also answers it. By symmetry, whoever gets to call the constructor
for const objects also gets the right to call its destructor.

The usual argument against it is that when the destructor is called,
the object is still const. This argument ignores the inherent symmetry
between constructor and destructor.

`What', you say? `They is no such symmetry!'. Of course there is.
Otherwise, making a destructor private would not have sufficed to
deny clients the right to create auto objects of the class:

class T {
~T();
};


void f() {
T t; // Destructor inaccessible. Illegal.
}

The fact that this is illegal shows the symmetry.

>What is it about this issue that leads people to advance arguments so
>clearly stated, but logically bankrupt?

As long as people on this list (some, unlike me, with lots of
credentials behind them) continue to invest in this particular
venture, I would not consider their position `bankrupt'.

---------------------------------------------
Ziv Caspi
zi...@netvision.net.il

Andrew Koenig

unread,
Sep 13, 1999, 3:00:00 AM9/13/99
to

In article <37DB2E5D...@ihug.co.nz>,
Ross Smith <ros...@ihug.co.nz> wrote:

>> The question is not whether new(const foo) is useful, but rather
>> whether new T is useful, where T is a template parameter that
>> happens to be bound to const foo.

>Well, is it? Can you give an example -- I mean a realistic example where
>it's actually useful, not just some toy code that uses it?

I can, but I won't -- because writing examples that go beyond toy
code is hard work. What I will do is sketch the form that
such an example might take.

Consider a container class:

template<class T> class Container {
// ...
};

that works by allocating and deallocating objects of type T.

The question is how much work the author of this class has to do
in order to enable a Container<const T> to work properly.

I think that if you fill in the details of this sketch, you will
understand the problem.
--
Andrew Koenig, a...@research.att.com, http://www.research.att.com/info/ark

Andrew Koenig

unread,
Sep 13, 1999, 3:00:00 AM9/13/99
to

I'll be out of town during the coming week (if you're curious, visit
http://www.accu.org/events/public/jacc99sep.htm), so I won't be able
to participate in this discussion for a while. Before I go, I'd like
to explain my current thinking on the delete-of-const issue.

The fundamental fact on which my thinking rests is that there is a
distinction between an object and the contents of the object, and that
it is possible to define operations on either one independently of
the other. In particular, allocation and deallocation are something
you do to an object, and modification is something you do to the
value of the object.

The Unix file system is a fine example of a design that makes this
distinction explicit. For example, files do not have names -- it is
only links that have names. There is no such operation as deleting a
file -- the closest equivalent is removing a link, and doing so does
not delete the file if any other links exist to it. Moreover, you do
not need write permission on a file in order to remove a link to it --
all you need is write permission on the directory that contains the
link.

So the Unix file system allows you to have files that you can
``remove'' but not modify, or files that you can modify but not
remove, or both, or neither.

If C++ had built-in garbage collection, the analogy to the Unix file
system would be clearer, because then delete would presumably only
invalidate the pointer, and the garbage collector would take care of
the object later. Nevertheless, I believe that the analogy still
operates.

One reason I believe that the analogy is valid -- and therefore that
creation/deletion is independent of modification -- is the behavior of
local variables. There is absolutely no question that it is legal to
deallocate a local variable, even if it is const:

{
const int* p;
{
const int x = 3;
p = &x;
}
}

In this example, we have created a const object, placed its address in
a pointer, and then deallocated the object, leaving the pointer
invalid -- even though it is a pointer to const.

As another example, there is no question that it is legal to delete a
dynamically allocated object that contains a const member:

class X {
public:
X(): a(42) { }
private:
const int a;
};

int main()
{
X* xp = new X;
delete xp;
}

Here, "delete xp;" deallocates a const object, namely member `a' of
the object that was allocated by `new X'. Again, there is no question
that this is legal, because if it were not, there is no cast that
could render it so.

So then why is there even a question? The most common argument I have
heard is that it is useful to be able to guarantee, for example, that
a function will not deallocate the object to which one of its
arguments points. So, for example,

extern void f(const T*);

It would be nice to be able to look at that definition and see that
not only will f not modify the object to which its argument points,
but it won't delete that object either.

Moreover, I agree with that argument! It *would* be nice! So why do
I continue to think that the committee did the right thing to make
this guarantee impossible to offer?

The reason is that I think that if such a guarantee is tied to `const',
it isn't strong enough to be useful. In particular, as long as there
is no way to define a function that *can* modify the object it is
given, but *cannot* delete it, I consider such a guarantee to be of
limited practical value.

In other words, I want to be able to call a function such as

X x;
read(cin, &x);

and be able to declare `read' in such a way as to be confident that it
will not delete x, even though it can modify it.

To make this discussion more concrete, let me imagine an addition to
the language that would do what I think is necessary for such a
guarantee to be genuinely useful. I will add a new property of
objects, analogous to `const' and `volatile', that, when present,
indicates that the object can be deleted with `delete'. For want of a
better term, I will call this property `dynamic'.

As with `const' and `volatile', `dynamic' will have to propagate
through the type system. Obviously the only objects that will be
`dynamic' are those allocated by `new'. Moreover, there will be a
conversion from `dynamic T*' (i.e. pointer to `dynamic T') to T*, but
not the other way around. So, for example:

{
T* tp;
dynamic T* dtp;
T t;

tp = &t; // OK
dtp = &t; // error -- attempt to convert
// T* to dynamic T*
dtp = new T; // OK
tp = dtp; // OK
delete dtp; // OK
delete tp; // error -- *tp not dynamic
}

Clearly, any arithmetic on a pointer would drop the dynamic property
from the result, as would converting a pointer to any other type --
except for derived-to-base conversion where the base had a virtual
destructor.

Given this property, it should be clear that

extern void f0(T*); // modify, no delete
extern void f1(const T*); // neither modify nor delete
extern void f2(dynamic T*); // both modify and delete
extern void f3(const dynamic T*); // delete, no modify

are all meaningful, and it is possible to imagine circumstances in
which any of them might be useful.

Well, enough hypothetical design. C++ doesn't have `dynamic'. So the
question about whether to allow delete of a const pointer boils down
to this question:

Should we say that `const' implies not `dynamic' ?

If you like, we have four functions that we might like to be able to
declare, but only one bit to express which one we are declaring.
An answer of yes implies that we can declare f1 and f2, but not f0 and
f3. An answer of no implies that we can declare f2 and f3, but not f0
and f1. (not f0 because without `dynamic' as a language feature, we
have to behave as if it is present by default, otherwise we could
never delete any objects at all).

In other words, without `dynamic' as part of the language, we can
always declare f2, and we can never declare f0. The question boils
down to whether it is more useful to be able to declare f1 or f3.

My answer is that it more useful to be able to declare f3, and that if
I cannot declare f0, I find little utility in being able to declare f1.
It is certainly possible to design the language so as to allow f1 and
not f3, but ultimately I think it comes down to a matter of which
tradeoff one prefers, and I continue to prefer the ability to define
f2 to the ability to define f1.

I continue to think that this is an issue about which reasonable
people can disagree.

Ziv Caspi

unread,
Sep 13, 1999, 3:00:00 AM9/13/99
to

On 12 Sep 99 02:59:32 GMT, n...@nospam.cantrip.org (Nathan Myers)
wrote:

>Valentin Bonnard <Bonn...@wanadoo.fr> wrote:


>>Ross Smith wrote:
>>
>>> Does
>>> anyone have any practical use for new(const foo)?
>>
>>Maybe to create a const instance. Does anybody have
>>any use of auto const Foo foo; // Foo is a UDT
>
>You make a const auto object to bind const& arguments to.

But you don't have to. You can just as easily bind const& to
a non-const auto variable. Creation of const auto variables
just relieves you of the need to bind const references
to them to disallow their change, which is not needed for
new'd objects (as you'd have to bind to a reference
anyway).

So, Valentin's question still stands: What is the use of
auto const variables?

[...]

---------------------------------------------
Ziv Caspi
zi...@netvision.net.il

Dave Harris

unread,
Sep 13, 1999, 3:00:00 AM9/13/99
to

fra...@robinton.demon.co.uk (Francis Glassborow) wrote:
> However deleter() might well be shipped as object code in which case you
> no longer have source to search. Personally I am very unhappy at any
> use of const_cast<> that allows some process that is not known to be
> safe. IOWs whenever you use const_cast<> it should be to remove a const
> that has been acquired during some process of parameter passing though
> the original object was not so protected. Removing const from an object
> that was defined as such is always deeply suspect.

I agree. Deleter() is a dangerous function to have around. I wouldn't want
it habitually used by template authors.

Deleter at least has the virtue that it can only delete, and we can search
for it at the same time we search for const_cast. Even with all its
problems I think it is better than the current situation in which the
const_cast is supplied silently by the compiler. I also don't think it
would be needed often. Eg I don't think any of my current code would need
it.

Are you aware of the other, less general but more safe idioms that have
been suggested? For example, wrapper classes like:

template <typename Type>
class ConstPtr {
Type *p;
public:
ConstPtr( Type *pp ) : p(pp) {}
~ConstPtr() { delete p; }

const Type &operator*() { return *p; }
const Type *operator->() { return p; }
};

Thus replace:
const int *p( new int( 42 ) );
delete p;

with:
ConstPtr<int> p( new int( 42 ) );

The non-const pointer is never exposed, but kept behind the scenes for use
in the delete expression. It is type-safe; there are no casts.

Admittedly helpers like this aren't always enough. We could do with a
general mechanism for stripping the top-level const off a type. Meanwhile
we could just write more template specialisations and/or be careful about
how we instantiate them, and use deleter() as a last-ditch fall-back.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

Dave Harris

unread,
Sep 13, 1999, 3:00:00 AM9/13/99
to

a...@research.att.com (Andrew Koenig) wrote:
> Consider a container class:
>
> template<class T> class Container {
> // ...
> };
>
> that works by allocating and deallocating objects of type T.
>
> The question is how much work the author of this class has to do
> in order to enable a Container<const T> to work properly.

I'm not convinced such a thing makes sense. In the same way that if I
wrote:

class X {
private:
~X();
};

I would not expect to be allowed Container<X>. However, if the thing does
make sense, we can accomplish it with a different template:

template<typename T>
class ConstContainer {
public:
const T &operator[]( int i ) const;
//...
};

which always adds a "const" to every T it exposes. This is a little extra
work, admittedly, but at least we can define one container in terms of the
other using inheritance.

Another approach is to use a wrapper class for T. I posted an example a
few hours ago.

nik...@my-deja.com

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to
In article <37dad1df$0$2...@nntp1.ba.best.com>,

n...@nospam.cantrip.org (Nathan Myers) wrote:
>
> Lisa Lippincott <lisa_li...@advisories.com> wrote:
> >Most people will also agree that the demolition of an object should
> >reverse the creation. Thus:
> >
> > First, the lifetime of the object ends;
> > second, the body of the destructor is executed;
> > third, subobjects are destroyed;
> > finally, storage is released.
> >
> >Looking at this chronology, it's clear that the destructor does not
> >operate on the object -- instead, it operates on the subobjects. The
> >question of whether the former object was const is moot.
> >
> >So there is no sense in which destruction or deletion is a non-const
> >operation.
>
> This assertion has been made repeatedly by apologists for the (now)
> status quo. As in the case of most purely theoretical arguments, it
> completely ignores the consequences of the alternatives. It is in
the
> consequences where we find the differences that affect programming.
>
> Still, if you insist on the theoretical approach, then we can say
that,
> yes, once the destructor is entered the question of const or non-
const
> is moot. However, the question was of who should be allowed to cause
> the destructor to be entered. Thus, Lippincott's argument purely
begs
> the question.

Theoretical arguments have been advanced on both sides. It has
been argued several times that deleting a pointer-to-const is
a const violation because a delete expression calls a non-const
method: the destructor. Lisa's argument demonstrates the flaw
in this reasoning. A delete expression first ends the lifetime
of an object and _then_ calls the destructor, at which time
constness is not an issue.

The question remains whether "ending the existence of" an object
constitutes "changing" that object. Some would say yes, and view
it as so obvious that no further analysis is possible. But if you
change a thing, you should be able to inspect it before and after
and find some difference; you cannot inspect an object that has
been deleted. You might say you changed the object's 'existence',
but this is not an attribute of the object. If an object had an
'existence' attribute it would always be true, for you cannot
refer at all to a non-existent object. Whether a particular object
exists or not is not part of the state of that object, but a fact
about the state of the program as a whole.

My point is not to argue either side on the basis of theory, but
to show that pure logic does not dictate, one way or the other,
whether const should apply to deletion.

> The decision to break const for the case of delete-expressions may
turn
> out to be a minor wart in the language. What is far worse, in my
view,
> is the breakdown in logical reasoning processes it seems to bring
> repeatedly to the surface. Lippincott's attempt at argument by
begging
> the question was far from the first, and probably will not be the
last,
> that we have seen here.
>

> What is it about this issue that leads people to advance arguments so
> clearly stated, but logically bankrupt?

Now who's begging the question? Neither the narrow definition of
const in the current language, nor the broader definition you
prefer is inherently more "correct" or valid than the other. Your
reference to the "decision to break const" distorts the issue by
framing it as a choice between a "broken" design and a valid one.

The best design would be the one that struck the best balance
between convenience and safety. I tend to think the committee made
the right choice, mainly because preventing deletion of pointer-
to-const would not buy much safety. For example:

void Func()
{
T info;
GetInfo(&info); // changes info
}

Even if const applied to deletion, we still could not prevent
GetInfo from "accidentally" deleting its argument. It would be
more useful if we could simultaneously grant change access and
deny delete access. This reinforces my view that state (which
is the domain of const) is separate from existence/duration
(which is not expressed in the type system).


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

---

Nathan Myers

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to
Ziv Caspi <zi...@netvision.net.il> wrote:
>n...@nospam.cantrip.org (Nathan Myers) wrote:
>>Lisa Lippincott <lisa_li...@advisories.com> wrote:
>>>... it's clear that the destructor does not

>>>operate on the object -- instead, it operates on the subobjects. The
>>>question of whether the former object was const is moot.
>>>
>>>So there is no sense in which destruction or deletion is a non-const
>>>operation.
>
>>
>> ... once the destructor is entered the question of const or non-const
>>is moot. However, the question was of who should be allowed to cause
>>the destructor to be entered. Thus, Lippincott's argument purely begs
>>the question.
>
>It also answers it. By symmetry, whoever gets to call the constructor
>for const objects also gets the right to call its destructor.

To beg the question is to _fail_ to answer it. You may assume the
conclusion only if use it to show a contradiction in arguments for it.
You cannot support a conclusion by assuming it. This is elementary.

>The usual argument against it is that when the destructor is called,
>the object is still const. This argument ignores the inherent symmetry
>between constructor and destructor.

Again, purely theoretical arguments, e.g. about "symmetry", fail
to consider engineering consequences. You can make up arguments
to support anything at all if you carefully avoid discussing
consequences.

>>What is it about this issue that leads people to advance arguments so
>>clearly stated, but logically bankrupt?
>

>As long as people on this list (some, unlike me, with lots of
>credentials behind them) continue to invest in this particular
>venture, I would not consider their position `bankrupt'.

The expression "good money after bad" comes to mind. Those who
argued in committee for breaking const have an emotional investment
in the breakage. Advancing logically-unsound arguments in its
favor, and failing to respond to demonstrations that it was a
serious mistake, are the nature of the continued investment.

Dave Harris

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to
a...@research.att.com (Andrew Koenig) wrote:
> extern void f0(T*); // modify, no delete
> extern void f1(const T*); // neither modify nor delete
> extern void f2(dynamic T*); // both modify and delete
> extern void f3(const dynamic T*); // delete, no modify
> [...]

> The question boils down to whether it is more useful to be able
> to declare f1 or f3.
>
> My answer is that it more useful to be able to declare f3, and that if
> I cannot declare f0, I find little utility in being able to declare f1.
> It is certainly possible to design the language so as to allow f1 and
> not f3, but ultimately I think it comes down to a matter of which
> tradeoff one prefers, and I continue to prefer the ability to define
> f2 to the ability to define f1.

I agree with most of your view, but at this point we depart company. To me
f3 is obscure. I am not allowed to access the object after f3 has deleted
it, so why should I care if f3 modifies it first? I won't be able to see
the change.

f1, on the other hand, would be tremendously useful. In virtually every
place where I declare a const argument, my intended meaning is "neither
modify nor delete", as f1.

I agree f0 would be nice. In truth, deletion is quite a rare thing and
most of my functions are like f0 or f1. However, I don't see that it's
essential. I don't understand why the lack of f0 makes f1 useless. Given
that we must have f2 and can only have 1 other, I'd opt for f2 and f1.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

---

Ross Smith

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to
Andrew Koenig wrote:
>
> Consider a container class:
>
> template<class T> class Container {
> // ...
> };
>
> that works by allocating and deallocating objects of type T.
>
> The question is how much work the author of this class has to do
> in order to enable a Container<const T> to work properly.

Why would you expect a Container<const T> to work properly? I wouldn't.

To me this looks like a solution in search of a problem. You're just
pushing the "what use is it?" question back one layer of indirection.
What use is deleting const objects? Well, you need it to make containers
of const objects work. OK then, what use are containers of const
objects?

There have been several arguments in this thread along the lines of "if
you can delete a const foo* then you can do X", but none of them, to me
at least, have satisfactorily explained why you would want to do X.

--
Ross Smith <ros...@ihug.co.nz> The Internet Group, Auckland, New Zealand
========================================================================
"There are many technical details that make Linux attractive to the
sort of people to whom technical details are attractive." -- Suck

Jeff Rife

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to
Andrew Koenig (a...@research.att.com) wrote:

> [excellent analogy and summary snipped]


>
> extern void f0(T*); // modify, no delete
> extern void f1(const T*); // neither modify nor delete
> extern void f2(dynamic T*); // both modify and delete
> extern void f3(const dynamic T*); // delete, no modify

And, to jump in before people say, "yeah, dynamic...that's what we
need...let's get ready for when we can revise the standard", we,
unfortunately, cannot have it.

Since what we declare now as f0 above works like the f2 above (we can,
after all, modify and delete something declared as just a T*), we
would have to add a keyword to the language that would do the opposite
of Andrew's very insightful "dynamic".

I'm going to duck as I completely jokingly suggest that we generate
yet another overload of the keyword "static", and use:

extern void f0(T*); // both modify and delete
extern void f1(const T*); // delete, no modify
extern void f2(static T*); // modify, no delete
extern void f3(const static T*); // neither modify nor delete

--
Jeff Rife | "Hello. My name is Inigo Montoya. You killed
19445 Saint Johnsbury Lane | my father. Prepare to die."
Germantown, MD 20876-1610 |
Home: 301-916-8131 | -- Inigo Montoya, "The Princess Bride"
Work: 301-770-5800 Ext 5335 |
---

Nathan Myers

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to
Ziv Caspi <zi...@netvision.net.il> wrote:
> n...@nospam.cantrip.org (Nathan Myers) wrote:
>>Valentin Bonnard <Bonn...@wanadoo.fr> wrote:
>>>Ross Smith wrote:
>>>
>>>> Does
>>>> anyone have any practical use for new(const foo)?
>>>
>>>Maybe to create a const instance. Does anybody have
>>>any use of auto const Foo foo; // Foo is a UDT
>>
>>You make a const auto object to bind const& arguments to.
>
>But you don't have to. You can just as easily bind const& to
>a non-const auto variable.

It is in fact _not_ "just as easy" to bind a const& to a
non-const auto variable.

Suppose you have functions declared as follows:

void f(T&);
void f(T const&);

and you want to call the second one.

You fill in the blanks.

Francis Glassborow

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to
In article <MPG.12479407...@news.nabs.net>, Jeff Rife
<we...@nabs.net> writes

>Since what we declare now as f0 above works like the f2 above (we can,
>after all, modify and delete something declared as just a T*), we
>would have to add a keyword to the language that would do the opposite
>of Andrew's very insightful "dynamic".
>
>I'm going to duck as I completely jokingly suggest that we generate
>yet another overload of the keyword "static", and use:
>
>extern void f0(T*); // both modify and delete
>extern void f1(const T*); // delete, no modify
>extern void f2(static T*); // modify, no delete
>extern void f3(const static T*); // neither modify nor delete

And this would be far from the first time that we have had to provide a
keyword for the reverse concept (explicit springs to mind)


Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Nathan Myers

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to

Valentin Bonnard <Bonn...@wanadoo.fr> wrote:

>Nathan Myers wrote:
>> Valentin Bonnard <Bonn...@wanadoo.fr> wrote:
>> >Ross Smith wrote:
>> >
>> >> Does
>> >> anyone have any practical use for new(const foo)?
>> >
>> >Maybe to create a const instance. Does anybody have
>> >any use of auto const Foo foo; // Foo is a UDT
>>
>> You make a const auto object to bind const& arguments to.
>
>You can do that with a non const object as well:
>
>Foo foo;
>const Foo& ref = foo;

_ref_ above is not an argument. Please re-read.

The question was whether it was useful to make a const auto
object. The answer was, yes, it is useful: it is easier to
bind const& arguments to them. Any further quibbling is
silly; it's useful to make pointers-to-const, and it's
useful to make const auto objects. Enough said.

>The reasons for making auto objects of UDT const and
>dynamic objects const are basically the same.

False. There is no dynamic object without a pointer. In use,
it is the declaration of the pointer that matters, not of the
object itself.

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Nathan Myers

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to

Dave Harris <bran...@cix.co.uk> wrote:
>Deleter() is a dangerous function to have around. I wouldn't want
>it habitually used by template authors.
>
>Deleter at least has the virtue that it can only delete, and we can search
>for it at the same time we search for const_cast. Even with all its
>problems I think it is better than the current situation in which the
>const_cast is supplied silently by the compiler.

In discussing this we should not lose sight of the fact that deleter<>()
was introduced as a demonstration that the breaking const didn't buy
anything. Of course the existence of a deleter template (as such)
in real code should set off alarm bells.

I would expect to see the const_cast<> at an appropriate place in
a well-designed resource-management apparatus, so there would
be no question of its correctness.

Francis Glassborow

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to

In article <7rjh8g$2mv$1...@nnrp1.deja.com>, nik...@my-deja.com writes

>But if you
>change a thing, you should be able to inspect it before and after
>and find some difference; you cannot inspect an object that has
>been deleted. You might say you changed the object's 'existence',
>but this is not an attribute of the object. If an object had an
>'existence' attribute it would always be true, for you cannot
>refer at all to a non-existent object. Whether a particular object
>exists or not is not part of the state of that object, but a fact
>about the state of the program as a whole.

In a reference counted object it is perfectly reasonable to require that
the object data is not changed while allowing its reference count
(presumably mutable) to be lowered by one. In such cases it is possible
to delete an object and examine the data afterwards.


Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Siemel B. Naran

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to

On 13 Sep 1999 16:17:06 GMT, Andrew Koenig <a...@research.att.com> wrote:

>The question is how much work the author of this class has to do
>in order to enable a Container<const T> to work properly.

Even within the current rules, a std::container<const T> is improper.

Consider
std::container<const int> v(3,0);
which creates a container of 3 elements, each initialized to '0'.


I.

To find space for 3 elements, the container calls the default
allocator's allocate function. In template form, it is

T * allocator<T>::allocate(size_type N) {
return static_cast<T *>(std::malloc(N*sizeof(T)));
}

For T=="int const", it is

int const * allocator<int const>::allocate(size_type N) {
return static_cast<int const *>(std::malloc(N*sizeof(int const)));
}

As static_cast cannot be used to add or remove const qualifiers, the
cast is illegal.

II.

To construct each element, the container calls the default allocator's
construct function. In template form, it is

void allocator<T>::construct(T * place, const T& obj)
{
new (place) T(obj);
}


For T=="int const", it is

void allocator<int const>::construct(int const * place, const const int& obj)
{
new (place) (const int)(obj);
}

The call to placement new is illegal as it tries to modify a read-only
location.

The compiler must ignore two consts in a row in implicit template
instantiation, so the declaration is legal. Incidentally, this means
that the following two functions are the same

typename allocator<T>:: pointer allocator<T>::address( reference);
typename allocator<T>::const_pointer allocator<T>::address(const_reference);

Thus the rule that the compiler must ignore two consts in a row could
be an error in the standard. If it is an error, then the declaration
is illegal too.

(BTW, there was an issue that in expressions like
std::bind2nd(std::mem_fun(&X::show),std::cout) we end up with a
reference to a reference. For example, because the function show
is X::show(std::ostream&), the constructor of std::binder2nd is
std::binder2nd::binder2nd(const Oper&, const std::ostream& &);
Forcing a reference to a reference to be a reference may cause
problems as above.)

III.

To destroy the 3 elements, the container calls the default
allocator's destroy function. In template form, it is

void allocator<T>::destroy(T * obj) { obj->~T(); }

For T=="int const", it is

void allocator<int const>::destroy(int const * obj) { obj->~T(); }

It seems to me that this should be illegal, but by the current rules
of C++, it is legal.

IV.

To deallocate the 3 elements, the container calls the default
allocator's deallocate function. In template form, it is

void allocator<T>::deallocate(T * array) { std::free(array); }

For T=="int const", it is

void allocator<int const>::deallocate(int const * array) {std::free(array);}

This is an error because the single argument of std::free is a "void *".

--
--------------
siemel b naran
--------------

blargg

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to

In article <37DD73C6...@ihug.co.nz>, Ross Smith <ros...@ihug.co.nz> wrote:

> Andrew Koenig wrote:
> >
> > Consider a container class:
> >
> > template<class T> class Container {
> > // ...
> > };
> >
> > that works by allocating and deallocating objects of type T.
> >

> > The question is how much work the author of this class has to do
> > in order to enable a Container<const T> to work properly.
>

> Why would you expect a Container<const T> to work properly? I wouldn't.

You could insert objects into it and remove them. You couldn't modify them
in-place. Seems reasonable enough to me.

> To me this looks like a solution in search of a problem. You're just
> pushing the "what use is it?" question back one layer of indirection.
> What use is deleting const objects? Well, you need it to make containers
> of const objects work. OK then, what use are containers of const
> objects?

Not that I consider this a real problem. Just use delete_const() to delete
the freestore.

template<typename T>
inline void delete_const( T const* p ) {
delete const_cast<T*> (p);
}

> There have been several arguments in this thread along the lines of "if
> you can delete a const foo* then you can do X", but none of them, to me
> at least, have satisfactorily explained why you would want to do X.

Right.

I'm retracting any opinion on this matter now. I can only begin to imagine
what the discussions must have been like on the committee :-)

Lisa Lippincott

unread,
Sep 15, 1999, 3:00:00 AM9/15/99
to
Nathan Myers <n...@nospam.cantrip.org> raises my dander:

> The decision to break const for the case of delete-expressions may turn
> out to be a minor wart in the language. What is far worse, in my view,
> is the breakdown in logical reasoning processes it seems to bring
> repeatedly to the surface. Lippincott's attempt at argument by begging
> the question was far from the first, and probably will not be the last,
> that we have seen here.
>
> What is it about this issue that leads people to advance arguments so
> clearly stated, but logically bankrupt?

As an accredited logician, I take exception to this reproach.

I suggest that, should you examine my argument in a less heated mood,
you will find its logic sound. The argument does, however, rest on two
nonlogical assumptions, with which you are free to disagree.

The first assumption is that the current rules defining object lifetime
are well-chosen. As they apply to the matter at hand, I think they are,
and I've seen no argument to the contrary here.

The second, and perhaps more controversial, assumption is that the meaning
we wish to ascribe to const is simply "a const object may not be modified."

Others -- though notably not Mr. Myers -- have argued that executing an
object's destructor modifies the object, and that the second assumption
therefore forbids deleting a const object. I earned Mr. Myers's scorn
by pointing out the flaw in this argument: that it relies on the ill-defined
notion of modifying an object which no longer exists.

On the other hand, it is my impression that Mr. Myers simply rejects the
second assumption. As a practical matter, he would rather have const
mean "may not be modified, and may not have its lifetime ended explicitly."
With this opinion I respectfully disagree; the meaning of const should not
be further laden. Further, I disdain his repeated suggestion that all
arguments contrary to his opinion are illogical.

--Lisa Lippincott
---

Ziv Caspi

unread,
Sep 15, 1999, 3:00:00 AM9/15/99
to
On 14 Sep 99 10:35:09 GMT, n...@nospam.cantrip.org (Nathan Myers)
wrote:

>Ziv Caspi <zi...@netvision.net.il> wrote:
>>
>>But you don't have to. You can just as easily bind const& to
>>a non-const auto variable.
>
>It is in fact _not_ "just as easy" to bind a const& to a
>non-const auto variable.
>
>Suppose you have functions declared as follows:
>
> void f(T&);
> void f(T const&);
>
>and you want to call the second one.

If I understand your point (here and elsewhere in this thread),
you seem to be arguing that there's no such thing as a const
object -- only const references/pointers.

Then you wouldn't mind this blanks fill-ins:

T t;
f( (T const&)t );

---------------------------------------------
Ziv Caspi
zi...@netvision.net.il

James...@dresdner-bank.com

unread,
Sep 15, 1999, 3:00:00 AM9/15/99
to

In article <37dde728$0$2...@nntp1.ba.best.com>,
n...@nospam.cantrip.org (Nathan Myers) wrote:

> >The usual argument against it is that when the destructor is called,
> >the object is still const. This argument ignores the inherent
> >symmetry between constructor and destructor.

> Again, purely theoretical arguments, e.g. about "symmetry", fail
> to consider engineering consequences. You can make up arguments
> to support anything at all if you carefully avoid discussing
> consequences.

Andy Koenigs long posting pretty much convinced me that the theory
supported allowing delete on const objects. But if theoretical concerns
had affected the definition of C++, it would be a far different
language. C++ has always come down on the pragmatic, getting things
done, side. And on this side, my experience strongly suggests that
allowing deletion of const objects removes a certain number of very
useful compile time checks, without giving any real practical advantages
in return. As Nathan says, the engineering consequences favor
forbidding it.

I'd be interested in seeing a *real* example where allowing it helps.
The template examples are interesting, but from this point of view, only
valid if you can show me a real template which would really be
instantiated on a const type -- off hand, I can't think of one.

> >>What is it about this issue that leads people to advance arguments
> >>so clearly stated, but logically bankrupt?

> >As long as people on this list (some, unlike me, with lots of


> >credentials behind them) continue to invest in this particular
> >venture, I would not consider their position `bankrupt'.

> The expression "good money after bad" comes to mind. Those who
> argued in committee for breaking const have an emotional investment
> in the breakage. Advancing logically-unsound arguments in its
> favor, and failing to respond to demonstrations that it was a
> serious mistake, are the nature of the continued investment.

This is coming very close to an ad hominum argument -- I'm surprised
that the moderators let it through. I think Andy is 100% sincere in his
position; you can speculate as to hidden psycological motivations, but
this forum is not the place for it (and probably neither you nor I are
really trained for it either).

--
James Kanze mailto: James...@dresdner-bank.com
Conseils en informatique orientée objet/
Beratung in objekt orientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

James...@dresdner-bank.com

unread,
Sep 15, 1999, 3:00:00 AM9/15/99
to

In article <slrn7trsa1....@localhost.localdomain>,
sbn...@uiuc.edu wrote:

> On 13 Sep 1999 16:17:06 GMT, Andrew Koenig <a...@research.att.com> wrote:

> >The question is how much work the author of this class has to do
> >in order to enable a Container<const T> to work properly.

> Even within the current rules, a std::container<const T> is improper.


>
> Consider
> std::container<const int> v(3,0);
> which creates a container of 3 elements, each initialized to '0'.

> I.
>
> To find space for 3 elements, the container calls the default
> allocator's allocate function. In template form, it is
>
> T * allocator<T>::allocate(size_type N) {
> return static_cast<T *>(std::malloc(N*sizeof(T)));
> }
>
> For T=="int const", it is
>
> int const * allocator<int const>::allocate(size_type N) {
> return static_cast<int const *>(std::malloc(N*sizeof(int const)));
> }
>
> As static_cast cannot be used to add or remove const qualifiers, the
> cast is illegal.

What makes you say that? According to 5.2.9/6: "The inverse of any
standard conversion sequence, other than the lvalue-to-rvalue,
array-to-pointer, function-to-pointer, and boolean conversions, can be
performed explicitly using static_cast subject to the restriction that
the explicit conversion does not cast away constness." Since int const*
to void* is a standard conversion, and void* to int const* doesn't cast
away const, it would seem legal. (As an aside, I wonder whether the
standard isn't missing something about casting away volatile, as well.
int* to volatile void* is legal, but I hope that static_cast< int* >(
volatile void* ) isn't.)

> II.

> To construct each element, the container calls the default allocator's
> construct function. In template form, it is
>
> void allocator<T>::construct(T * place, const T& obj)
> {
> new (place) T(obj);
> }
>
> For T=="int const", it is
>
> void allocator<int const>::construct(int const * place, const const int& obj)
> {
> new (place) (const int)(obj);
> }

> The call to placement new is illegal as it tries to modify a read-only
> location.

No. There is no such thing as a read-only location for a constructor --
if an implementation does arrange to turn on write protection for
individual objects, it must arrange for it to be turned off during
constructors and destructors. (At present, I know of no implementation
which will put an object with user-defined constructors or destructors
in write protected memory.)

However, placement new takes a void*, and there is no implicit
conversion from T const* to void*, only to void const*. So this
effectively fails.

> The compiler must ignore two consts in a row in implicit template
> instantiation, so the declaration is legal. Incidentally, this means
> that the following two functions are the same
>
> typename allocator<T>:: pointer allocator<T>::address(reference);
> typename allocator<T>::const_pointer allocator<T>::address(const_reference);

And if a fonction is defined twice, it is an error -- excellent point.

> Thus the rule that the compiler must ignore two consts in a row could
> be an error in the standard. If it is an error, then the declaration
> is illegal too.

> (BTW, there was an issue that in expressions like
> std::bind2nd(std::mem_fun(&X::show),std::cout) we end up with a
> reference to a reference. For example, because the function show
> is X::show(std::ostream&), the constructor of std::binder2nd is
> std::binder2nd::binder2nd(const Oper&, const std::ostream& &);
> Forcing a reference to a reference to be a reference may cause
> problems as above.)

I seem to remember a special exception for this case -- a reference to a
reference resolves to a reference to the referred to object.

> III.

> To destroy the 3 elements, the container calls the default
> allocator's destroy function. In template form, it is

> void allocator<T>::destroy(T * obj) { obj->~T(); }

> For T=="int const", it is

> void allocator<int const>::destroy(int const * obj) { obj->~T(); }

> It seems to me that this should be illegal, but by the current rules
> of C++, it is legal.

> IV.
>
> To deallocate the 3 elements, the container calls the default
> allocator's deallocate function. In template form, it is

> void allocator<T>::deallocate(T * array) { std::free(array); }

> For T=="int const", it is

> void allocator<int const>::deallocate(int const * array) {std::free(array);}

> This is an error because the single argument of std::free is a "void *".

As far as I can tell, this implementation is non-conforming; the
standard requires calling ::operator delete. But since the argument
type is still void*, and int const* does *not* convert implicitly to
void*, the problem remains unchanged.

Anyway, I think you've effectively proven that templates using separate
allocation and construction cannot be instantiated on a const type
unless they contain explicit const_cast's.

Ziv Caspi

unread,
Sep 15, 1999, 3:00:00 AM9/15/99
to

On 14 Sep 99 10:31:09 GMT, n...@nospam.cantrip.org (Nathan Myers)
wrote:

>Ziv Caspi <zi...@netvision.net.il> wrote:
>>n...@nospam.cantrip.org (Nathan Myers) wrote:
>>>Lisa Lippincott <lisa_li...@advisories.com> wrote:
>>>>... it's clear that the destructor does not
>>>>operate on the object -- instead, it operates on the subobjects. The
>>>>question of whether the former object was const is moot.
>>>>
>>>>So there is no sense in which destruction or deletion is a non-const
>>>>operation.
>>
>>>
>>> ... once the destructor is entered the question of const or non-const
>>>is moot. However, the question was of who should be allowed to cause
>>>the destructor to be entered. Thus, Lippincott's argument purely begs
>>>the question.
>>
>>It also answers it. By symmetry, whoever gets to call the constructor
>>for const objects also gets the right to call its destructor.
>
>To beg the question is to _fail_ to answer it. You may assume the
>conclusion only if use it to show a contradiction in arguments for it.
>You cannot support a conclusion by assuming it. This is elementary.

1. Lippincott's argument was based on (mirror) symmetry between
the constructor and destructor. Although it did not directly address
the issue you raised, extending the argument to cover it is a
trivial effort (regardless of whether you think it is true or not).
That's why I said "it also answers it". Isn't it you who like to
say "fill in the blanks"?

2. Rereading your answer, I don't believe you addressed the issue
I raised.

>
>>The usual argument against it is that when the destructor is called,
>>the object is still const. This argument ignores the inherent symmetry
>>between constructor and destructor.
>
>Again, purely theoretical arguments, e.g. about "symmetry", fail
>to consider engineering consequences. You can make up arguments
>to support anything at all if you carefully avoid discussing
>consequences.
>

>>>What is it about this issue that leads people to advance arguments so
>>>clearly stated, but logically bankrupt?
>>
>>As long as people on this list (some, unlike me, with lots of
>>credentials behind them) continue to invest in this particular
>>venture, I would not consider their position `bankrupt'.
>
>The expression "good money after bad" comes to mind. Those who
>argued in committee for breaking const have an emotional investment
>in the breakage. Advancing logically-unsound arguments in its
>favor, and failing to respond to demonstrations that it was a
>serious mistake, are the nature of the continued investment.

Perhaps. On the other hand, the "demonstrations" you mention
failed to convince several people, including myself. Perhaps
it is because we are all "silly". Then again, perhaps not. Is that
why you have not addressed any of my arguments?

---------------------------------------------
Ziv Caspi
zi...@netvision.net.il

James...@dresdner-bank.com

unread,
Sep 15, 1999, 3:00:00 AM9/15/99
to

In article <slrn7trsa1....@localhost.localdomain>,
sbn...@uiuc.edu wrote:

> To find space for 3 elements, the container calls the default
> allocator's allocate function. In template form, it is
>
> T * allocator<T>::allocate(size_type N) {
> return static_cast<T *>(std::malloc(N*sizeof(T)));
> }

Is this implementation legal. In 20.4.1.1, it says (concerning
allocate): "Uses ::operator new(size_t)". This would seem to forbid a
direct call to malloc. On the other hand, this text is in a field
called Notes (but it is not marked as a note normally is): is it
normative? And if not, why do the notes say something that the standard
doesn't guarantee?

--
James Kanze mailto: James...@dresdner-bank.com
Conseils en informatique orientée objet/
Beratung in objekt orientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

Darin Adler

unread,
Sep 15, 1999, 3:00:00 AM9/15/99
to
James...@dresdner-bank.com wrote:

> According to 5.2.9/6: "The inverse of any standard conversion sequence,
> other than the lvalue-to-rvalue, array-to-pointer, function-to-pointer, and
> boolean conversions, can be performed explicitly using static_cast subject
> to the restriction that the explicit conversion does not cast away

> constness." [...] (As an aside, I wonder whether the standard isn't missing


> something about casting away volatile, as well. int* to volatile void* is
> legal, but I hope that static_cast< int* >( volatile void* ) isn't.)

In response to your aside: The standard is OK.

Section 5.2.11 defines (in paragraphs 8-12) "casting away constness".
The definition includes casting in ways that remove volatile as well.

-- Darin
---

nik...@my-deja.com

unread,
Sep 15, 1999, 3:00:00 AM9/15/99
to

In article <9W06JmAI...@robinton.demon.co.uk>,

Francis Glassborow <fran...@robinton.demon.co.uk> wrote:
>
> In article <7rjh8g$2mv$1...@nnrp1.deja.com>, nik...@my-deja.com writes
> >But if you
> >change a thing, you should be able to inspect it before and after
> >and find some difference; you cannot inspect an object that has
> >been deleted. You might say you changed the object's 'existence',
> >but this is not an attribute of the object. If an object had an
> >'existence' attribute it would always be true, for you cannot
> >refer at all to a non-existent object. Whether a particular object
> >exists or not is not part of the state of that object, but a fact
> >about the state of the program as a whole.
>
> In a reference counted object it is perfectly reasonable to require that
> the object data is not changed while allowing its reference count
> (presumably mutable) to be lowered by one. In such cases it is possible
> to delete an object and examine the data afterwards.

You can _dereference_ an object and examine it afterwards
(if it has not been deleted), but it is never possible to
_delete_ an object and examine it afterwards. Reference
counting does not change the meaning of delete; it is
merely one mechanism for determining when deletion occurs.

Whether 'delete pointer-to-const' should be legal is a
practical question, the answer to which does not follow
inevitably from the internal logic of the language.
Phrases like "the decision to break const in the case
of delete" therefore frame the question appropriately.

I actually think it would be nice if the type system
offered some way of declaring a pointer that could not
be deleted, but I don't see broadening the definition
of const as a very good way to accomplish this. In my
experience, constness simply doesn't correlate very
well with whether a pointer should be "deletable". If
anything, delete permission should be controlled by a
separate type qualifier, something like this:

T* new p1; // 'delete p1' is ok
T* new[] p2; // 'delete[] p2' is ok
T* static p3; // can't delete p3
T* p4; // no restrictions

The real issue is with new and delete, not const.
Containers and smart pointers make it increasingly
possible to use new and delete very sparingly. So if
you want to find potentially dangerous code to review
carefully, "new" and "delete" might be better
candidates for a search than "const_cast".


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

Lisa Lippincott

unread,
Sep 16, 1999, 3:00:00 AM9/16/99
to
James Kanze <James...@dresdner-bank.com> wrote:
> And on this side, my experience strongly suggests that
> allowing deletion of const objects removes a certain number of very
> useful compile time checks, without giving any real practical advantages
> in return. As Nathan says, the engineering consequences favor
> forbidding it.
>
> I'd be interested in seeing a *real* example where allowing it helps.
> The template examples are interesting, but from this point of view, only
> valid if you can show me a real template which would really be
> instantiated on a const type -- off hand, I can't think of one.

Consider auto_ptr. In my view, disallowing deletion of const objects
would require a specialization of auto_ptr for const types, and I'm not
even sure how to write that specialization.

On the other hand, if there were a separate cv-qualifier denoting the
ability to delete an object, its use would greatly clarify the interface
to auto_ptr for both const and non-const types.

--Lisa Lippincott
---

Anders J. Munch

unread,
Sep 16, 1999, 3:00:00 AM9/16/99
to
Andrew Koenig wrote in message ...
[...]
>The argument that I consider the strongest, and to which I have
>never seen a counterargument that is the least bit convincing,
>is that if you are allowed to create an object, you should be
>allowed to destroy it.

You have never seen a good counterargument against this? Strange, because
IMHO your argument misses the point entirely.

I'd like to see you try invoking construction on an object .. something like
this?<g>

void f(SomeClass const * ob)
{ ob->SomeClass(); }

This is meaningless, of course, because object creation is something you
invoke on the *class*. Object destruction, on the other hand, is something
you invoke on the *instance*. Thus access to construct an object depends on
your access to the class; access to delete an object depends on your access
to the object instance in question.

Since 'const' controls the access to an *instance*, there is no reason why
it couldn't restrict the instance-oriented delete operation.

Then there is the example:

>That is, in
>
>template<class T> void f(args) {
>
>// ...
>
>}
>
>I should be able to say
>
>T* tp = new T;
>delete tp;

There is only a problem if T is a const type. Let's call the non-const type
U:
class U;
typedef T const U;
Why would you want to create a const object?! What is happening here is
that you are creating an object of class U, a 'U*' and assigning it to a 'U
const*' variable. In doing that you have accidentally thrown away
accesibility. I think you would be much happier simply creating a U object
and assigning it to a U* variable. No accessibility lost, and even if
delete const is outlawed you would have no problem.

It seems that for the purpose of writing templates, C++ is lacking a means
of getting the non-const type from a const type. But that is a different
issue, and allowing const delete doesn't "fix" it in general. For an
example of another issue with constness problems in templates, see the
recent "typeof" discussion.

- Anders

Anders J. Munch

unread,
Sep 16, 1999, 3:00:00 AM9/16/99
to

Andrew Koenig wrote in message ...
[...]
>One reason I believe that the analogy is valid -- and therefore that
>creation/deletion is independent of modification -- is the behavior of
>local variables. There is absolutely no question that it is legal to
>deallocate a local variable, even if it is const:
>
> {
> const int* p;
> {
> const int x = 3;
> p = &x;
> }
> }
[...]

But *you* are not deallocating x in this example. The compiler is
generating code to destroy the object, to which x refers. If you were to
write to invoke a destructor x.~int(), it would be a different matter.

A way of understanding const locals and statics is to see yourself as a
client of compiler-owned objects which you have only limited access to. On
entry into the block the compiler generates an object (non-const), allows
you *const* access to the object under the name you chose, and on exit from
the block deletes the (non-const) object.

Example:

Programmer writes:
{
SomeClass const myObject;
..do something with myObject..
}

Compiler transforms this into:
{
SomeClass __myObject;
__usercode(__myObject);
}
void __usercode(SomeClass const& myObject)
{
..do something with myObject..
}

For the most part this transformation is semantically neutral, and yet, the
const auto has disappeared.

The same view can be applied to const members: That the real member is a
hidden non-const, but what you see under the name you chose for the member
is just a const reference to the real thing.

Under this view, all real objects are non-const, but sometimes we only see
them as const.

- Anders
(The meaning of "42.~int();" is left as an exercise to the galactic
traveler.)

Anders J. Munch

unread,
Sep 16, 1999, 3:00:00 AM9/16/99
to

Lisa Lippincott wrote in message
<090919992359200362%lisa_li...@advisories.com>...
[...]
>Looking at this chronology, it's clear that the destructor does not

>operate on the object -- instead, it operates on the subobjects. The
>question of whether the former object was const is moot.

>
>So there is no sense in which destruction or deletion is a non-const
>operation.


I'll concede that, let's take a look at the consequences:

Before destruction: If the complete object was const, then all the
subobjects were also const.

Destruction initiated: Complete object doesn't exist as such, however
subobjects still exist. As they still exist, the const property still
applies to them.

This means that destructors must satisfy the requirements of a const member
function - all non-mutable members considered const etc.

Since the destructor may be implemented elsewhere without satisfying this
requirement, it simply cannot be fulfilled. In short: delete cannot be
invoked on a const object.

- Anders

Siemel B. Naran

unread,
Sep 16, 1999, 3:00:00 AM9/16/99
to

On 15 Sep 1999 19:09:09 GMT, nik...@my-deja.com <nik...@my-deja.com> wrote:

>Whether 'delete pointer-to-const' should be legal is a
>practical question, the answer to which does not follow
>inevitably from the internal logic of the language.

It seems to me that the decisions to allow or disallow
deleting a pointer to const are both equally logical.

>Phrases like "the decision to break const in the case
>of delete" therefore frame the question appropriately.

Agreed. Can anyone think of a practical reason why
being able to delete a pointer to const is a bad thing?

My original thought was that if I make a container of
pointers to existing objects like
std::deque<House const *> expensive_houses(const std::list<House>&);
then the 'const' would garauntee that no-one
accidentally deletes any houses.

But I soon realized that I want the same restriction
for pointers to non-const object.
std::deque<House *> expensive_houses(const std::list<House>&);
Still, users should not be able to delete houses in
the deque.

> T* new p1; // 'delete p1' is ok
> T* new[] p2; // 'delete[] p2' is ok
> T* static p3; // can't delete p3
> T* p4; // no restrictions

This is a great idea.
There is additional work for the compiler to do, but no runtime overhead.
There are some conversion rules, like "T *new" to "T *static" is ok, but
"T *static" to "T *new" is not.

// find all houses that are expensive
std::deque<House *static> expensive_houses(const std::list<House>&);

However, these extensions are not necessary because one can write
almost-smart pointer classes to do the same, although it would be
nice to have these almost-smart pointer classes standardized.

// find all houses that are expensive
std::deque<static_ptr<House>> expensive_houses(const std::list<House>&);


where

template <class T>
class static_ptr {
public:
explicit static_ptr(T * ptr) : ptr(ptr) { }
T * operator->() const { return ptr; }
private:
T * ptr;
};


--
--------------
siemel b naran
--------------

Siemel B. Naran

unread,
Sep 16, 1999, 3:00:00 AM9/16/99
to

On 15 Sep 1999 15:56:40 GMT, James...@dresdner-bank.com
>In article <slrn7trsa1....@localhost.localdomain>,

>> Even within the current rules, a std::container<const T> is improper.

>> As static_cast cannot be used to add or remove const qualifiers, the
>> cast is illegal.
>
>What makes you say that? According to 5.2.9/6: "The inverse of any
>standard conversion sequence, other than the lvalue-to-rvalue,
>array-to-pointer, function-to-pointer, and boolean conversions, can be
>performed explicitly using static_cast subject to the restriction that
>the explicit conversion does not cast away constness." Since int const*
>to void* is a standard conversion, and void* to int const* doesn't cast
>away const, it would seem legal. (As an aside, I wonder whether the
>standard isn't missing something about casting away volatile, as well.
>int* to volatile void* is legal, but I hope that static_cast< int* >(
>volatile void* ) isn't.)

Did you make a typographical error? You say that "int const *" to
"void *" is a standard conversion. Sounds to me like it should be an
error. When I do "void * v=(int const *)0;", one of my compilers
(egcs) gives a warning and another gives an error (como EDG).

I read item 1 and it says that static_cast cannot be used to cast
away constness. Though it seems that static_cast can be used to add
const. Incidentally, neither of my two compilers allow it --
"int const * i=static_cast<int const *>((void*)0)" is an error.

I don't think static_cast should be allowed to add const because
there is no reason to allow it to do so. We already have the
implicit cast and const_cast for this purpose.

>> The call to placement new is illegal as it tries to modify a read-only
>> location.
>
>No. There is no such thing as a read-only location for a constructor --
>if an implementation does arrange to turn on write protection for
>individual objects, it must arrange for it to be turned off during
>constructors and destructors. (At present, I know of no implementation
>which will put an object with user-defined constructors or destructors
>in write protected memory.)
>
>However, placement new takes a void*, and there is no implicit
>conversion from T const* to void*, only to void const*. So this
>effectively fails.

Right.

>I seem to remember a special exception for this case -- a reference to a
>reference resolves to a reference to the referred to object.

template <class In, class Out>
struct Silly_t { void show(In &); void show(In const &); };

template <class In, class Out>
void Silly(Out (*func)(In), In & in) { Silly_t<In,Out>().show(in); }

void f(int &);
int main() { int i=0; Silly(&f,i); }

If a reference to a reference is an error, then the instantiation
void Silly(void (*func)(int&), int & & in) { ... }
is an error.

If a reference to a reference is a reference, then the instantiation
of function Silly is ok, but the instantiation of Silly_t is not.
struct Silly_t<void,int&> { void show(int & &); void show(int & const &); };
Both functions are the same function, hence a redeclaration error.

>As far as I can tell, this implementation is non-conforming; the
>standard requires calling ::operator delete. But since the argument
>type is still void*, and int const* does *not* convert implicitly to
>void*, the problem remains unchanged.

I'm not clear on this distinction between standard conversion and
implicit conversion.

>Anyway, I think you've effectively proven that templates using separate
>allocation and construction cannot be instantiated on a const type
>unless they contain explicit const_cast's.

Or you specify the allocator, eg:
std::container<const int,std::allocator<int>> c;
I'm not sure if this will work though, and I don't want to think about
it either :).

--
--------------
siemel b naran
--------------

Dave Harris

unread,
Sep 16, 1999, 3:00:00 AM9/16/99
to

lisa_li...@bigfix.com (Lisa Lippincott) wrote:
> Consider auto_ptr. In my view, disallowing deletion of const objects
> would require a specialization of auto_ptr for const types, and I'm not
> even sure how to write that specialization.

Auto_ptr is intended to delete objects, and if we're agreed that const
types may not be deleted then it doesn't make sense to instantiate
auto_ptr for them. To do so would introduce a type error. It should be
disallowed by default.

For example:

void proc1( const int *p ) {
std::auto_ptr<const int> pp( p );

} // *p deleted here?

attempts to do exactly what we want proc's signature to forbid. Please
don't add a specialisation of auto_ptr which makes this silently work.

Consider instead adding a const_auto_ptr which uses <type> instead of
<const type>, but which adds const to the type in its public methods. It
would allow you to write:

void test() {
const_auto_ptr<int> p( new int( 42 ) );
proc2( p );
}

void proc2( const_auto_ptr<int> p ) {
// *p = 99; -- would be a compile error; *p is const.

} // but *p is deleted here

const_auto_ptr<> is trivial to write and allows you to express what is
needed in a type-safe way. We can also write wrappers which make the
deletion optional to express what Andrew Koenig called "const but
dynamic", his "f3" signature.

Obviously we can do the other thing; with the current rules we can produce
a wrapper which exposes its object as const but not deletable. Functions
like proc1() could use it to promise non-deletion. Of course they could
never take its address or pass it to another function except in its
non-delete wrapper. The idiom would be incompatible with existing code.
Still, advocates of the old ARM rule should consider this if they think
non-deletability is so important.

With the right wrappers we can express almost anything, so we're really
talking about what the default should be. This comes down to two
questions:

1) Which would be most commonly needed?
2) Which is safest?

In my opinion, the answer in both cases is the old ARM rule: no deletion
through a const pointer.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

Lisa Lippincott

unread,
Sep 16, 1999, 3:00:00 AM9/16/99
to

Anders J. Munch <ande...@dancontrol.dk> wrote:
> Before destruction: If the complete object was const, then all the
> subobjects were also const.
>
> Destruction initiated: Complete object doesn't exist as such, however
> subobjects still exist. As they still exist, the const property still
> applies to them.

No, they were const only by virtue of being members of a const object.
Upon entry into the destructor, they are no longer members of any object.
Those which were not declared const are no longer const.

I think that's a straightforward reading of 3.9.3 [basic.type.qualifier].
One could image it being written otherwise, but it would make const
objects awfully hard to construct.

--Lisa Lippincott

Anders J. Munch

unread,
Sep 17, 1999, 3:00:00 AM9/17/99
to
Lisa Lippincott wrote in message
<160919991230311725%lisa_li...@bigfix.com>...

>
>Anders J. Munch <ande...@dancontrol.dk> wrote:
>> Before destruction: If the complete object was const, then all the
>> subobjects were also const.
>>
>> Destruction initiated: Complete object doesn't exist as such, however
>> subobjects still exist. As they still exist, the const property
still
>> applies to them.
>
>No, they were const only by virtue of being members of a const object.
>Upon entry into the destructor, they are no longer members of any
object.
>Those which were not declared const are no longer const.

Constness in function arguments is a contract between caller and callee,
and we are debating whether this contract allows destruction. Agreed?

Which one of the functions f1 and f2 below has the strictest contract?

struct S1
{
int const aMem;
~S1();
};

struct S2
{
int aMem;
~S2();
};

void f1(S1&);
void f2(S2 const &);


I would say that f2 should have the stricter contract, because here
constness applies to the complete object, whereas with f1 constness
applies only the member
aMem.

But with your semantics, f2 has a more lenient contract, because when
~S2() is invoked, aMem can be assigned to.

>I think that's a straightforward reading of 3.9.3
[basic.type.qualifier].
>One could image it being written otherwise, but it would make const
>objects awfully hard to construct.


Not necessarily. Objects created-as-const can be logically viewed as a
non-const object being created, except that only const-access is granted
under the given name.

Programmer writes:
SomeClass const x(initialization);

Compiler internally rewrites this into:
SomeClass __shadow_x(initialization);
SomeClass const & x = __shadow_x;

Under this view, const objects are never actually created or destroyed.

- Anders
---

Jeff Rife

unread,
Sep 17, 1999, 3:00:00 AM9/17/99
to
Anders J. Munch (ande...@dancontrol.dk) wrote:

> Not necessarily. Objects created-as-const can be logically viewed as a
> non-const object being created, except that only const-access is granted
> under the given name.
>
> Programmer writes:
> SomeClass const x(initialization);

But, this means that the implementation is free to put the object into
read-only memory. I know, it has to be writable to put it there, but by
changing processor protection on the memory area, many implementations
could do this, so by the time the rest of the user progam sees it, it
is in read-only memory.

This is entirely different from something like:

void func(SomeClass const * x)
{
SomeClass* y = const_cast<SomeClass*>(x);

y->Fred = 7;
}

Here, the compiler *cannot* assume that the object pointed to is actually
const. The function above *does* not invoke undefined behavior if
the object pointed to by x is not actually const like:

SomeClass z;
func(&z);

The const contract in the function declaration is being violated, and we
all agree that this is horrible programming, but it is legal, as long
as it is never called with an actual const object being pointed to.

> Compiler internally rewrites this into:
> SomeClass __shadow_x(initialization);
> SomeClass const & x = __shadow_x;
>
> Under this view, const objects are never actually created or destroyed.

Using the read-only-memory idea above, const objects are neither created
or destroyed, either. The implmentation creates an object, puts it in
a place that makes it const to the rest of the user-code, and eventually
destroys it there.

No matter how you word it, the implementation is doing things that are
out of the reach of the user-code. Because of that, you can't compare
what an implementation does behind the scenes to what user-code can do
in a conforming program.

--
Jeff Rife | Making files is easy under the UNIX operating
19445 Saint Johnsbury Lane | system. Therefore, users tend to create numerous
Germantown, MD 20876-1610 | files using large amounts of file space. It has
Home: 301-916-8131 | been said that the only standard thing about all
Work: 301-770-5800 Ext 5335 | UNIX systems is the message-of-the-day telling
| users to clean up their files.
| -- System V.2 administrator's guide

Anders J. Munch

unread,
Sep 17, 1999, 3:00:00 AM9/17/99
to

Lisa Lippincott wrote in message
<150919991344231146%lisa_li...@bigfix.com>...
>James Kanze <James...@dresdner-bank.com> wrote:

[...]


>> I'd be interested in seeing a *real* example where allowing it helps.

[...]


>Consider auto_ptr. In my view, disallowing deletion of const objects
>would require a specialization of auto_ptr for const types, and I'm not
>even sure how to write that specialization.

[...]

The question was: Why would you want to delete a pointer to something
const?

Since auto_ptr is simply a way of automating the delete process, this is
just forwarding the issue.

New question: Why would you want to create an auto_ptr to something
const?

Same question in a different guise.

- Anders

Jerry Leichter

unread,
Sep 17, 1999, 3:00:00 AM9/17/99
to

| Consider the sequence of steps involved in creating an object.
|
| First, storage is obtained;
| second, subobjects are constructed;
| third, the body of the constructor is executed;
| finally, the lifetime of the object begins. If the object is
| const, it may not be modified.
|
| It's pretty hard to argue for a different arrangement. Does anyone
| want to try?

"Lifetime" as generally used in discussing programming languages begins
as soon as the storage is allocated. That stack allocation works for
locals in "Algol-like" languages is generally explained by saying that
in this arrangement a variable's lifetime is the same as its scope.

C++ differs from most languages by having user-written constructors.
It's certainly the case that a variable is in *some* special state
between the time its storage is obtained and the time that its
constructor returns, but I see no reason to say that its lifetime has
not begun. If you *do* say that, how do you describe what "this" refers
to in a member function called from a constructor? It's one thing to
say that *constructors* operate on variables before their lifetimes
begin, but do you really want to say that a member function can be
called on an object outside of that object's lifetime?

| Most people will also agree that the demolition of an object should
| reverse the creation. Thus:
|
| First, the lifetime of the object ends;
| second, the body of the destructor is executed;
| third, subobjects are destroyed;
| finally, storage is released.


|
| Looking at this chronology, it's clear that the destructor does not
| operate on the object -- instead, it operates on the subobjects. The
| question of whether the former object was const is moot.

The same question arises here: It's perfectly OK for a destructor to
call member functions. If the destructor only operates on subobjects,
does that mean member functions it calls only operates on subobjects?
Does what a member function "operates on" depend on where the function
was called?

Hell, you don't even have to restrict yourself to member functions! A
constructor or destructor is perfectly free to pass "this" to any
function that takes an argument of the appropriate type. If a
constructor does this as the last statement in its body - or a
destructor does it as the first statement in its body - then in fact the
object is guaranteed to be in a fully constructed state (for its type) -
so this is as safe and correct as passing any other object of the
appropriate type. Are you saying any function in the program can
operate on objects "after their lifetime"? If so, your definition of
"lifetime" is more than a bit strange!
-- Jerry

James Kuyper

unread,
Sep 17, 1999, 3:00:00 AM9/17/99
to

Jerry Leichter wrote:
...

> "Lifetime" as generally used in discussing programming languages begins
> as soon as the storage is allocated. That stack allocation works for
> locals in "Algol-like" languages is generally explained by saying that
> in this arrangement a variable's lifetime is the same as its scope.

Irrelevant - the standard defines "lifetime", and that isn't the
definition it uses.

> C++ differs from most languages by having user-written constructors.
> It's certainly the case that a variable is in *some* special state
> between the time its storage is obtained and the time that its
> constructor returns, but I see no reason to say that its lifetime has
> not begun.

The rules about 'lifetime' were designed to cope with the
characteristics of fully constructed objects that have not yet started
being destroyed; it really doesn't matter what you call that period of
time - those rules are still needed.

Nathan Myers

unread,
Sep 19, 1999, 3:00:00 AM9/19/99
to

<nik...@my-deja.com> wrote:
>Whether 'delete pointer-to-const' should be legal is a
>practical question, the answer to which does not follow
>inevitably from the internal logic of the language.

Correct. Engineering consequences matter.

>Phrases like "the decision to break const in the case
>of delete" therefore frame the question appropriately.

That was how the question was framed when the decision was made.
"Const" had a particular meaning that affected (myriad) designs,
and those designs were broken when the meaning was changed.

>I actually think it would be nice if the type system
>offered some way of declaring a pointer that could not
>be deleted, but I don't see broadening the definition
>of const as a very good way to accomplish this.

There is no prospect of "broadening" the definition of const.
The definition _was_ narrowed. The arguments that effected its
narrowing, and how they could have been considered persuasive,
are the topic.

>The real issue is with new and delete, not const.
>Containers and smart pointers make it increasingly
>possible to use new and delete very sparingly. So if
>you want to find potentially dangerous code to review
>carefully, "new" and "delete" might be better
>candidates for a search than "const_cast".

This is an interesting point: now that const has been broken, our
coding standards should flag use of "delete" itself as being equally
as suspicious as "const_cast". Under the new regime we must wrap
our pointers up in class objects to get the same protection we had,
before the breakage, with ordinary pointers.

This is the most succinct description I have seen of the engineering
consequences of having broken const.

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Nathan Myers

unread,
Sep 19, 1999, 3:00:00 AM9/19/99
to comp-s...@moderators.isc.org

Lisa Lippincott <lisa_li...@bigfix.com> wrote:
>Nathan Myers <n...@nospam.cantrip.org>
>> The decision to break const for the case of delete-expressions may turn
>> out to be a minor wart in the language. What is far worse, in my view,
>> is the breakdown in logical reasoning processes it seems to bring
>> repeatedly to the surface. Lippincott's attempt at argument by begging
>> the question was far from the first, and probably will not be the last,
>> that we have seen here.
>>
>> What is it about this issue that leads people to advance arguments so
>> clearly stated, but logically bankrupt?
>
>As an accredited logician, I take exception to this reproach.
>
>I suggest that, should you examine my argument in a less heated mood,
>you will find its logic sound. The argument does, however, rest on two
>nonlogical assumptions, with which you are free to disagree.

It is, specifically, the assumptions that are under discussion.

Advancing a logical argument in favor of one's assumptions, based
on those same assumptions, is logically invalid.

>As a practical matter, he would rather have const mean "may not be
>modified, and may not have its lifetime ended explicitly." With this
>opinion I respectfully disagree; the meaning of const should not
>be further laden.

In all compilers prior to the (quite late) committee decision, const
had the meaning Ms. Lippincott describes as "further laden". In other
words, an important quality of "const" was discarded. It was discarded
based on unsound reasoning -- argument by analogy, as it happened.

Subsequently, apologists have advanced increasingly sophistic arguments
why breaking const was good, in every case arguing without reference to
engineering consequences.

From an engineering standpoint, they all amount to "const was
inconvenient when you wanted to do what it was meant to prevent".

Prior to the breakage, many interfaces were designed under the
promise that handing over a pointer-to-const could be safe.
The code didn't break, but the promise did, and the change
broke all the many designs that depended on the promise.

> Further, I disdain his repeated suggestion that all
> arguments contrary to his opinion are illogical.

Only the contrary arguments I have _seen_ were illogical. More to
the point, they have all carefully avoided addressing the software
engineering consequences of the change. I.e. it doesn't so much matter
that they were illogical, because they have also been irrelevant.

Carefully avoiding engineering consequences does not lead to good
design decisions, or sound arguments. Again, I am disappointed
to find otherwise clear-thinking people indulging this way.

Nathan Myers

unread,
Sep 19, 1999, 3:00:00 AM9/19/99
to comp-s...@moderators.isc.org

Andrew Koenig <a...@research.att.com> wrote:
>
>The fundamental fact on which my thinking rests is that there is a
>distinction between an object and the contents of the object, and that
>it is possible to define operations on either one independently of
>the other.

C++ doesn't make this distinction. Some objects have members, but
member access is not at issue here.

> In particular, allocation and deallocation are something
>you do to an object, and modification is something you do to the
>value of the object.

Allocation and deallocation occur outside the lifetime of the object,
so do not involve the object proper at all. In the case of member
and auto objects, there is no issue: the language determines when the
lifetime ends. Hence, attempts at analogies to members or autos can
only reveal, or produce, a deep confusion.

At issue is who should be allowed to end the lifetime of the object
when the language doesn't determine it. No one can scrupulously reason
this out without considering engineering consequences.

>The Unix file system is a fine example of a design that makes this
>distinction explicit.

This false analogy was suggested in committee, and apparently influenced
votes. It has been addressed before in this forum. To address it
(<sigh>) again:

Unlinking files in Unix is _precisely_ equivalent to assigning zero to
a pointer in a garbage-collected run-time environment. That is, the
Unix file system supports garbage collection; it has _no_ analog of
"delete". Because no one has argued that

void f(T const* tp) { tp = 0; }

should be, or should have been, forbidden, the analogy is at best
misleading.

To claim that an analogy to the Unix file system supported breaking
const is to argue that ordinary users should be allowed to "really
delete" Unix files regardless of the number of references to them.
This facility has been frequently requested for Unix, and has been
as frequently rejected, for sound reasons.

That the Unix file system analogy has surfaced again, here, after it
was so soundly demolished before, is worrisome.

>One reason I believe that the analogy is valid -- and therefore that
>creation/deletion is independent of modification -- is the behavior of
>local variables. There is absolutely no question that it is legal to
>deallocate a local variable, even if it is const:
>
> { const int* p; { const int x = 3; p = &x; } }
>

>In this example, we have created a const object, placed its address in
>a pointer, and then deallocated the object, leaving the pointer
>invalid -- even though it is a pointer to const.

"We" have *not* deallocated the object. Its lifetime ended independently
of anything we did. This false analogy apparently also influenced votes.

As presented in committee, the analogy claimed was that the following

{ T const t; } // implicit t.T::~T() invoked by compiler
{ T const* tp = new T; delete tp; }

should be equivalently legal. This certainly looks reasonable, but the
example is deeply misleading. The analogy stated in words is less
so: "Because the compiler handles destruction of auto objects which go
out of scope, _any_ code _anywhere_ (e.g. code in the same block where
it was created) should be able to delete _any_ dynamically allocated
object, regardless of constness."

The verbal description makes clear that the code example as constructed
distracts us from the real consequences of the change.

>As another example, there is no question that it is legal to delete a
>dynamically allocated object that contains a const member:
>
> struct X { X(); const int a; };
> int main() { X* xp = new X; delete xp; }
>
>Here, "delete xp;" deallocates a const object, namely member `a' of
>the object that was allocated by `new X'. Again, there is no question
>that this is legal, because if it were not, there is no cast that
>could render it so.

"delete xp;" does _not_, in fact, deallocate a const object. It
destroys a non-const X object. After the (compiler-generated) X
destructor ends, the member `a' no longer exists, just as a local
variable no longer exists once it goes out of scope. At issue is
whether the delete-expression (and, thus, to enter X's destructor)
should be legal under various circumstances; anything that happens
after the destructor is entered can have no bearing on the issue.

The sloppy thinking that led to Andrew's example above is just what
I meant to call to the attention of readers in this forum.

>So then why is there even a question? The most common argument I have
>heard is that it is useful to be able to guarantee, for example, that
>a function will not deallocate the object to which one of its
>arguments points. So, for example,
>
> extern void f(const T*);
>
>It would be nice to be able to look at that definition and see that
>not only will f not modify the object to which its argument points,
>but it won't delete that object either.

Not only "would" it be nice; the guarantee *was* actively useful.
Many thousands of interfaces were designed based on that guarantee.
Those designs were then broken when their invariants could no longer
be enforced.

The minutes don't suggest that breakage of existing designs had any
influence on the decision.

>Moreover, I agree with that argument! It *would* be nice! So why do
>I continue to think that the committee did the right thing to make
>this guarantee impossible to offer?
>
>The reason is that I think that if such a guarantee is tied to `const',
>it isn't strong enough to be useful. In particular, as long as there
>is no way to define a function that *can* modify the object it is
>given, but *cannot* delete it, I consider such a guarantee to be of
>limited practical value.
>
>In other words, I want to be able to call a function such as
>
> X x;
> read(cin, &x);
>
>and be able to declare `read' in such a way as to be confident that it
>will not delete x, even though it can modify it.

In other words, because the language lacks a feature Andrew would like,
he defends breaking another, unrelated feature, despite that such
breakage got the language no closer to having the feature he would like.

>I continue to think that this is an issue about which reasonable
>people can disagree.

Each of the arguments Andrew raises above has been demolished before,
in this forum. Andrew never defended the demolished arguments, yet he
has dragged them out again.

Evidently even very intelligent people can disagree unreasonably.
I find that very interesting, and it's why I raised this topic again.
Something about this subject brings out the worst in some very smart
individuals' reasoning powers.

If it has any lessons for us, it may be that we must insist on an
engineering analysis of the consequences of language changes, and
refuse to rely on woolly analogies. Even very bright people are
easily led astray by appealing abstract arguments.

Nathan Myers

unread,
Sep 19, 1999, 3:00:00 AM9/19/99
to

Siemel B. Naran <sbn...@uiuc.edu> wrote:

>On 15 Sep 1999 19:09:09 GMT, nik...@my-deja.com <nik...@my-deja.com> wrote:
>
>>Whether 'delete pointer-to-const' should be legal is a
>>practical question, the answer to which does not follow
>>inevitably from the internal logic of the language.
>
>... Can anyone think of a practical reason why

>being able to delete a pointer to const is a bad thing?

Elementary. The argument does depend on some familiarity with
library design.

Imagine you are designing a library which maintains an internal object
of a public type. Prior to the breakage of const it was perfectly safe
for the library to hand a pointer to this object off to an (untrusted)
client of the library, because a pointer-to-const was inviolate. A
client was _incapable_ of violating any of the library's invariants
(except, as usual, by casting).

Now, to get the same level of safety you must define a wrapper class,
with forwarding functions, and release only instances of this wrapper
class.

What important language feature did we get in exchange for this pervasive
increase in complexity of libraries?

Siemel B. Naran

unread,
Sep 20, 1999, 3:00:00 AM9/20/99
to

On 19 Sep 1999 15:33:43 GMT, Nathan Myers <n...@nospam.cantrip.org> wrote:
>Siemel B. Naran <sbn...@uiuc.edu> wrote:

>>... Can anyone think of a practical reason why
>>being able to delete a pointer to const is a bad thing?
>
>Elementary. The argument does depend on some familiarity with
>library design.
>
>Imagine you are designing a library which maintains an internal object
>of a public type. Prior to the breakage of const it was perfectly safe
>for the library to hand a pointer to this object off to an (untrusted)
>client of the library, because a pointer-to-const was inviolate. A
>client was _incapable_ of violating any of the library's invariants
>(except, as usual, by casting).

I think my example of a function returning a container<House const *>
was such an example. The function, usually a member of class
List_of_Houses, passes houses to other clients for them to look at.
The const is an assurance that these other clients won't be able to
delete these House pointers.

But what about the more general case where we must pass a pointer to
non-const House? Now what's there to prevent clients from delete the
pointer? Nothing. That's why I'm not convinced that this argument
about whether or not we should be able to delete a pointer to const
is a useful argument.

On the other hand, it is rare to pass a pointer to non-const House
to outside clients, at least in my experience. The reason is that
when outside clients modify the House the class that owns the
Houses, namely class List_of_Houses, must know of the change so
that it can maintain its invariants. For example, it may keep the
Houses in alphabetical order, or it may maintain some sort of
internal index (so that if you change a House, it must change the
index), or maybe it must update the field that is the total value
of all the houses.


>Now, to get the same level of safety you must define a wrapper class,
>with forwarding functions, and release only instances of this wrapper
>class.

I think we need write only an operator-> and operator*. I posted a
class static_ptr in a previous post.


>What important language feature did we get in exchange for this pervasive
>increase in complexity of libraries?

--

--------------
siemel b naran
--------------

Andrew Koenig

unread,
Sep 20, 1999, 3:00:00 AM9/20/99
to

In article <37DD73C6...@ihug.co.nz>,
Ross Smith <ros...@ihug.co.nz> wrote:

>> The question is how much work the author of this class has to do
>> in order to enable a Container<const T> to work properly.

>Why would you expect a Container<const T> to work properly? I wouldn't.

Because that is how I would ask a container to store values
that should not change while in the container, which is something
that I have certainly wanted to do in practice.
--
Andrew Koenig, a...@research.att.com, http://www.research.att.com/info/ark

Nathan Myers

unread,
Sep 20, 1999, 3:00:00 AM9/20/99
to
Siemel B. Naran <sbn...@uiuc.edu> wrote:
>Nathan Myers <n...@nospam.cantrip.org> wrote:
>>Siemel B. Naran <sbn...@uiuc.edu> wrote:
>
>>>... Can anyone think of a practical reason why
>>>being able to delete a pointer to const is a bad thing?
>>
>>Elementary. The argument does depend on some familiarity with
>>library design.
>>
>>Imagine you are designing a library which maintains an internal object
>>of a public type. Prior to the breakage of const it was perfectly safe
>>for the library to hand a pointer to this object off to an (untrusted)
>>client of the library, because a pointer-to-const was inviolate. A
>>client was _incapable_ of violating any of the library's invariants
>>(except, as usual, by casting).
>
>I think my example of a function returning a container<House const *>
>was such an example. ...

>But what about the more general case where we must pass a pointer to
>non-const House? Now what's there to prevent clients from delete the
>pointer? Nothing. That's why I'm not convinced that this argument
>about whether or not we should be able to delete a pointer to const
>is a useful argument.

This is just Koenig's latest argument, again: because the language
(still) lacks feature Y (a pointer type that allows non-const member
access but prevents deletion), it was good to break the more-frequently
useful, and long-established, feature X (a safer pointer type that
prevents both non-const member access and deletion).

Let's try it this argument in another quarter: C++ lacks the
"long long" type introduced into C0x. Therefore, we should make
the effect of arithmetic overflow on unsigned int undefined.
After all, if we don't support "long long", why help users to
code it themselves?

---

Francis Glassborow

unread,
Sep 20, 1999, 3:00:00 AM9/20/99
to
In article <slrn7uaduu....@localhost.localdomain>, Siemel B.
Naran <sbn...@uiuc.edu> writes

>But what about the more general case where we must pass a pointer to
>non-const House? Now what's there to prevent clients from delete the
>pointer? Nothing. That's why I'm not convinced that this argument
>about whether or not we should be able to delete a pointer to const
>is a useful argument.

Yes, I think I am coming round to the view that we should consider some
way of providing a 'may not delete through this pointer/reference'
property quite distinct from 'may not modify...'


Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Anders J. Munch

unread,
Sep 20, 1999, 3:00:00 AM9/20/99
to

Jeff Rife wrote in message ...

>Anders J. Munch (ande...@dancontrol.dk) wrote:
>
>> Not necessarily. Objects created-as-const can be logically viewed as a
>> non-const object being created, except that only const-access is granted
>> under the given name.
>>
>> Programmer writes:
>> SomeClass const x(initialization);
>
>But, this means that the implementation is free to put the object into
>read-only memory.

Good observation, I missed a nuance here. The contract introduced with
'const' in the definition is stronger than the contract for a reference
to const. So my rewrite isn't completely semantically neutral.

This doesn't affect the point I was trying to make, though: That
creating something as const can be viewed as the implementation creating
something non-const and publicizing only const access. (And this would
apply to *both* constness contracts.)

[...]


>No matter how you word it, the implementation is doing things that are
>out of the reach of the user-code. Because of that, you can't compare
>what an implementation does behind the scenes to what user-code can do
>in a conforming program.


I wasn't trying to describe implementation.

<formal style=pompous>
What if I demonstrated a way of mechanically transforming any C++
program with well-defined behaviour into a C++ program without any const
objects but with the same behaviour, in such a way that no ill-defined
C++ program by this transformation became well-defined?
Ignoring the difference between the two constness contracts, this is
exactly what I did. And in doing so I have shown that there is no
philosophical problem with the construction and destruction of const
objects, even if deletion of const is disallowed. Q.E.D.
</formal>

- Anders

It is loading more messages.
0 new messages