Topic: Why don't we have const constructors?
Author: Risto Lankinen <risto.lankinen@ntc.nokia.com>
Date: 1996/05/07 Raw View
Hi!
I already asked this question some time (~3 months) ago in comp.lang.c++,
but did not receive a satisfying answer. Consider the following example:
***** Example begins *****
#include <assert.h>
class C
{
C *p;
public:
C() : p(this) { } // OK! See note (1) below
C( const C &r ) : p(r.p) { p->p = 0; } // OK! See note (2) below
~C() { assert(p); }
};
int main()
{
const C c1; // OOPS! See note (3) below
C c2(c1); // BANG! See note (4) below
return 0;
};
***** Example ends *****
Note (1): The class contains a pointer to a _non-const_ object of its own
type. The pointer is initialized to point to itself. This should be OK.
Note (2): The copy constructor copies the copiee's pointer to itself, and
then modifies the thing pointed-to by the pointer. This should be legal,
because the pointer is a non-const.
Note (3): The default constructor is used to initialize a constant object,
and something bad happens!!! C++ allows initialization of a 'C *' using a
'this' pointer that really is a 'const C *', which essentially casts away
the constness without any warning, much less an error message.
Note (4): Mayday... mayday... my 'const C c1' has been modified!!! Note
that this could actually happen several thousands of lines apart from the
actual problem spot of initializing the 'const C'.
My suggestion/question: isn't it time to consider 'const constructors' to
differentiate between const and non-const 'this'-pointers in cases like my
example? Or, if not, how do I prevent this from happening with the current
standard? My example actually is a simplification of a real world problem
that once occurred to me with a global 'const LIST' object.
terv: Risto L.
--
Risto Lankinen / System Engineer ***************************************
Nokia Telecommunications, * 2 2 *
Cellular Data; Helsinki, Finland. * 2 -1 is PRIME! Now working on 2 +1 *
risto.lankinen@ntc.nokia.com ***************************************
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/05/07 Raw View
In article 663D@ntc.nokia.com, Risto Lankinen <risto.lankinen@ntc.nokia.com> writes:
>
>class C
>{
> C *p;
>
>public:
> C() : p(this) { } // OK! See note (1) below
> C( const C &r ) : p(r.p) { p->p = 0; } // OK! See note (2) below
>
> ~C() { assert(p); }
>};
> main()
>{
> const C c1; // OOPS! See note (3) below
> C c2(c1); // BANG! See note (4) below
> ...
>
>My suggestion/question: isn't it time to consider 'const constructors' to
>differentiate between const and non-const 'this'-pointers in cases like my
>example? Or, if not, how do I prevent this from happening with the current
>standard? My example actually is a simplification of a real world problem
>that once occurred to me with a global 'const LIST' object.
A const constructor doesn't seem to have much utility, because all you
could do would be preclude modifying the object's own data in the
body of the constructor. I don't see how the "p->p=0;" would be caught
even if you had a const constructor. The thing that 'p' points to is
not declared const, and so is presumed modifiable.
Or are you suggesting that a const constructor not be permitted to make any
assignments in its body TO OR THROUGH any of its members? That would be
overly restrictive, difficult to specify, and still would not solve the
problem. You have to allow the member-init-list to set values, and an
assignment through a pointer could happen there, perhaps indirectly.
The C++ committee examined proposals to add const constructors, but did not
find a way to make them useful or consistent.
IMHO, your real problem is the design of the copy ctor coupled const objects
that point to non-const items. Given
const List l;
even though I couldn't write
l.p = ...;
I could still write
l.p->p = ... // from a friend or member function
The "const List" concept doesn't make much sense to me. No amount of
const protection rules can guard against a faulty concept.
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/05/07 Raw View
Risto Lankinen <risto.lankinen@ntc.nokia.com> writes:
>I already asked this question some time (~3 months) ago in comp.lang.c++,
>but did not receive a satisfying answer.
What you have noticed is a loophole in the C++ system of `const'
protection. (You're not the first to notice; the C++ committee has
known about this loophole for a long time.)
>Note (1): The class contains a pointer to a _non-const_ object of its own
>type. The pointer is initialized to point to itself. This should be OK.
Yes, that's well-formed and has well-defined behaviour.
>Note (2): The copy constructor copies the copiee's pointer to itself, and
>then modifies the thing pointed-to by the pointer. This should be legal,
>because the pointer is a non-const.
Yes, that's well-formed.
>Note (3): The default constructor is used to initialize a constant object,
>and something bad happens!!! C++ allows initialization of a 'C *' using a
>'this' pointer that really is a 'const C *', which essentially casts away
>the constness without any warning, much less an error message.
Actually, that's not true. During the constructor, the type of the
object really is `C', not `const C'. The object only becomes const
when the constructor completes.
>Note (4): Mayday... mayday... my 'const C c1' has been modified!!!
Yes, that is of course undefined behaviour.
The problem is somewhat analagous to dereferencing a dangling pointer.
The pointer that you are dereferencing once referred to a non-const
object, but it doesn't anymore.
>My suggestion/question: isn't it time to consider 'const constructors' to
>differentiate between const and non-const 'this'-pointers in cases like my
>example?
No. Const constructors simply don't make sense in the C++ object model.
As noted above, an object only becomes const when execution of the constructor
completes.
It would of course be possible to design a different language which didn't
have this loophole. But now is certainly not the time to consider making
major revisions to the C++ object model.
>Or, if not, how do I prevent this from happening with the current
>standard?
Convince your compiler vendors to add a compiler warning whenever a
constructor lets a non-const pointer or reference to the object being
constructed escape. Or program carefully, and beware of dangling
non-const pointers, just as you must beware of ordinary dangling
pointers in C++. There are tools such as ObjectCenter which can catch
both of these sorts of problems at runtime.
--
Fergus Henderson <fjh@cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3 | -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: Risto Lankinen <risto.lankinen@ntc.nokia.com>
Date: 1996/05/08 Raw View
Hi!
Steve Clamage wrote:
>
> In article 663D@ntc.nokia.com, Risto Lankinen <risto.lankinen@ntc.nokia.com> writes:
> >
> >class C
> >{
> > C *p;
> >
> >public:
> > C() : p(this) { } // OK! See note (1) below
> > C( const C &r ) : p(r.p) { p->p = 0; } // OK! See note (2) below
> >
> > ~C() { assert(p); }
> >};
> > main()
> >{
> > const C c1; // OOPS! See note (3) below
> > C c2(c1); // BANG! See note (4) below
> > ...
> >
> >My suggestion/question: isn't it time to consider 'const constructors' to
> >differentiate between const and non-const 'this'-pointers in cases like my
> >example?
>
> A const constructor doesn't seem to have much utility, because all you
> could do would be preclude modifying the object's own data in the
> body of the constructor. I don't see how the "p->p=0;" would be caught
> even if you had a const constructor. The thing that 'p' points to is
> not declared const, and so is presumed modifiable.
You're missing my point. Consider this (hypothetical!) example:
class C
{
C *p;
public:
C() : p(this) { } // OK!
C() const : p(this) { } // Error: 'C *' initialized w/'const C *'
C( const C &r ) : p(r.p) { p->p = 0; }
...
};
Now, either you make a different algorithm for the const constructor,
or you never construct const objects. In either case, you can assume
that the thing pointed-to by the copiee's 'r.p' really is a modifiable
object.
Another example:
void RegisterTheObjectForAutomaticModification( C *p )
{
cout << "Registered." << endl;
}
class C
{
public:
C() { RegisterTheObjectForAutomaticModification( this ); }
C() const { /* Even trying to register would fail!!! */ }
// Probably also needs a 'const destructor' to unregister properly
};
In both examples, constructor is a constructor is a constructor. It is
irrelevant whether they may modify their members in the body (however it
would make sense if the const constructor wouldn't be able to). The point
is that when the 'this' pointer of a const object is used as an rvalue,
it should be treated as a pointer to const accordingly. Otherwise there
is the danger that you leak out a non-const pointer to your const object
without getting any indication about when or where (or whether at all)
that is happening.
> Or are you suggesting that a const constructor not be permitted to make any
> assignments in its body TO OR THROUGH any of its members? That would be
> overly restrictive, difficult to specify, and still would not solve the
> problem. You have to allow the member-init-list to set values, and an
> assignment through a pointer could happen there, perhaps indirectly.
I would be happy with non-mandatory const constructors so that no existing
program would break. The rule might be that const constructors are called
only if they have been declared in the class header (ditto for destructors,
which I thought of only a moment ago writing the example).
> IMHO, your real problem is the design of the copy ctor coupled const objects
> that point to non-const items.
The existence of such items currently cannot be controlled, because the
constructor doesn't make the distinction of their 'this' pointing to a
non const vs. a const instance, and thus they can leak out non-const
pointers to themselves.
terv: Risto L.
--
Risto Lankinen / System Engineer ***************************************
Nokia Telecommunications, * 2 2 *
Cellular Data; Helsinki, Finland. * 2 -1 is PRIME! Now working on 2 +1 *
risto.lankinen@ntc.nokia.com ***************************************
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]