Topic: const constructors (Was: C++ seems to allow an implicit const-->non-const
Author: kristov@arcor.de (Christoph Schulz)
Date: Thu, 31 Aug 2006 20:20:38 GMT Raw View
Hello!
Ron Natalie wrote:
> During the construction of an object, the constness of *this is
> turned off (otherwise you could never really initialize a const).
> Since you keep a reference (a pointer would have done the same)
> to the non-const *this at this point, you are explicitly busting
> const.
I do not agree that it is done explicitly, as the class writer does not
have any change to detect whether its constructor is called for a const
or a non-const object. With this fact in mind, one comes to the
conclusion that storing a pointer/reference to "this" from within a
constructor is generally faulty. However, such code exists, think e.g.
about a possible implementation of a circular list where a Node's
default constructor initializes the "next" and "prior" pointer to
"this", indicating that the node is the only element in the list.
What I want to illustrate is that anyone who writes a constructor which
saves "this" somewhere must think of the possibility that the object
under construction is const. I don't think that C++ supports the
programmer in such a case in any way.
However, if C++ would allow to overload constructors on const/non-const,
one could get around the problem by forcing "this" in const constructors
to "T const*" except that member initialization lists would be
explicitly allowed. Of course, this is only some sort of a dream beacuse
it would break existing code which relies on the fact that a (then
non-const) constructor is used to initialize a const object, but it
would assist in finding errors like the one described above.
>> As far as I can see this
>> program is able to perform a const-->non-const conversion without
>> specifying a cast, namely by the use of a non-const reference
>> initialized by the constructor. That is alarming because implicit
>> const-->non-const conversions are evil.
>
> [...]
>
> Your solution is unworkable. You would have to allow the this pointer
> (or reference to *this) to be passed to other functions (either
> explicitly as an argument, or implicitly as being a call to another
> non-const non-static member function in the object). These
> functions would be succeptable to the same sort of const breaking
> if they stored the pointer/reference to this somewhere that persists
> past the duration of the construction.
As I tried to explain above, one would not allow non-static non-const
member functions from within a const constructor as the "this" pointer
would be "T const*"; the only way to initialize members of a const
object would be using the member initialization list. Even in the
initializer expressions "this" would have to be assumed as "const T *",
as otherwise you could store a non-const "this" in a non-const
pointer/reference member.
-- begin example --
struct A {
int i, j;
A () : i (0) { j = 1; } // non-const constructor
};
struct B {
int k;
B (int x) { k = x; } // (1)
B (int x) const : k (x) {} // (2)
B (int x, int y) const : k (x + y) { k--; }
// error: this->k cannot be modified through a pointer to const
};
struct C : A {
C () : A () {} // OK
C () const : A () {}
// not OK: A does not have a const default constructor
};
struct D {
D *d;
D () : d (this) {} // OK, no const problems possible
};
A a; // OK
const A a; // not OK, since A has no const constructor
B b (1); // OK, uses (1)
const B b (2); // OK, uses (2)
const D d; // not OK, since D has no const constructor
// => implicit const looseness solved
-- end example --
To make old code work, one could imagine a deprecated "T const*" => "T*"
conversion within const constructors (like for string literals which can
be converted to char*).
Regards,
Christoph
---
[ 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 ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Ron Natalie <ron@spamcop.net>
Date: Thu, 31 Aug 2006 16:59:23 CST Raw View
Christoph Schulz wrote:
> To make old code work, one could imagine a deprecated "T const*" => "T*"
> conversion within const constructors (like for string literals which can
> be converted to char*).
>
>
No, I do not believe you can.
struct C {
C() {
foo(this);
}
void foo();
void foo() const;
};
Which one would your "fix" execute?
---
[ 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 ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Christoph Schulz <kristov@arcor.de>
Date: Fri, 1 Sep 2006 09:57:59 CST Raw View
Hello!
Ron Natalie wrote:
> Christoph Schulz wrote:
>
>> To make old code work, one could imagine a deprecated "T const*" => "T*"
>> conversion within const constructors (like for string literals which can
>> be converted to char*).
>>
>>
> No, I do not believe you can.
>
> struct C {
> C() {
> foo(this);
> }
> void foo();
> void foo() const;
> };
>
> Which one would your "fix" execute?
The first one. The conversion mentioned above would only kick in if not
otherwise possible, i.e., for overload resolution it would be handled
almost as an ellipsis (worse than everything else), with the exception
that the ellipsis would be even worse. To compare with 13.3.3.2, one
would have:
standard < user-defined < const-conversion from above < ellipsis
As the const-to-non-const-conversion would not count as a user-define
conversion, 13.3.1/5 would not have to be modified.
All this would make your example well-defined (for me, at least): For
the first method there is a direct match of type Identity/Exact Rank (C
* ==> C *), and for the second one a const-to-non-const-conversion would
be necessary (C * ==> C const*). The second variant is worse than the
first one, so it is discarded, and we have a best match.
Regards,
Christoph
---
[ 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 ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Christoph Schulz <kristov@arcor.de>
Date: Sat, 2 Sep 2006 16:46:32 CST Raw View
Hello!
>> No, I do not believe you can.
>>
>> struct C {
>> C() {
>> foo(this);
>> }
>> void foo();
>> void foo() const;
>> };
>>
>> Which one would your "fix" execute?
>>
>
> Neither of the above.
> Chris
Of course you're right! I assumed "foo();" in my other post, not
"foo(this);".
Regards,
Christoph
---
[ 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 ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: ron@spamcop.net (Ron Natalie)
Date: Sat, 2 Sep 2006 21:47:48 GMT Raw View
Kri wrote:
> Uzytkownik "Ron Natalie" <ron@spamcop.net> napisal w wiadomosci
> news:44f75521$0$24214$9a6e19ea@news.newshosting.com...
>> Christoph Schulz wrote:
>>
>>> To make old code work, one could imagine a deprecated "T const*" => "T*"
>>> conversion within const constructors (like for string literals which can
>>> be converted to char*).
>>>
>>>
>> No, I do not believe you can.
>>
>> struct C {
>> C() {
>> foo(this);
>> }
>> void foo();
>> void foo() const;
>> };
>>
>> Which one would your "fix" execute?
>>
>
> Neither of the above.
> Chris
>
C() {
foo();
}
Sorry about that, I was typing faster than I was thinking.
---
[ 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 ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]