Topic: converting this to base-class pointer


Author: a9804814@unet.univie.ac.at ("Thomas Mang")
Date: Tue, 25 Oct 2005 22:49:04 GMT
Raw View
Hello,

12.7/2 reads:

"To explicitly or implicitly convert a pointer (an lvalue) referring to an
object of class X to a pointer (reference)
to a direct or indirect base class B of X, the construction of X and the
construction of all of its direct or
indirect bases that directly or indirectly derive from B shall have started
and the destruction of these classes
shall not have completed, otherwise the conversion results in undefined
behavior."

Therefore, this (nonsense) code snippet looks valid:

struct Base
{
Base(Base*){}
};

struct Derived : Base
{
Derived() : Base(this){}
};


This is basically a short version of the example in 12.7/2 also using
imlicitly a derived* -> base* conversion.


However, in 3.8 there is:

3.8/1:
"The lifetime of an object of type T begins
when:
- storage with the proper alignment and size for type T is obtained, and
- if T is a class type with a non-trivial constructor (12.1), the
constructor call has completed."

And 3.8/5 says:
"Before the lifetime of an object has started but after the storage which
the object will occupy has been allocated
 ....
If the object will be or was of a non-POD class type, the program has
undefined behavior if:
.
- the pointer is implicitly converted (4.10) to a pointer to a base class
type, or".


So one para says it's OK, the other says it's undefined behavior. What am I
missing / misinterpreting?


Thomas





---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Marc Schoolderman <squell@alumina.nl>
Date: Wed, 26 Oct 2005 14:46:02 CST
Raw View
Thomas Mang wrote:

Cutting away from your quote to reveal:

> "To .. implicitly convert a pointer .. referring to an
> object of class X to a pointer .. to a .. base class B of X, the construction
> of X .. shall have started ..., otherwise the conversion results in
undefined
> behavior."

Your example converts 'this' (a Derived*) to a Base* while the
construction of Derived has not yet started - so it is undefined.

> struct Base
> {
> Base(Base*){}
> };
>
> struct Derived : Base
> {
> Derived() : Base(this){}
> };

~Marc.

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Thomas Mang" <a9804814@unet.univie.ac.at>
Date: Wed, 26 Oct 2005 19:19:41 CST
Raw View

"Marc Schoolderman" <squell@alumina.nl> schrieb im Newsbeitrag
news:435fc035$0$716$5fc3050@dreader2.news.tiscali.nl...
> Thomas Mang wrote:
>
> Cutting away from your quote to reveal:
>
> > "To .. implicitly convert a pointer .. referring to an
> > object of class X to a pointer .. to a .. base class B of X, the
construction
> > of X .. shall have started ..., otherwise the conversion results in
> undefined
> > behavior."
>
> Your example converts 'this' (a Derived*) to a Base* while the
> construction of Derived has not yet started - so it is undefined.


Okay, maybe I have simplified a bit too much.
BTW, at which point has the constructor started? I searched for it, but
couldn't find it.

Well, here two other versions. In case there's again something wrong with
that snippet, pay attention to the example in 12.7/2, especially to the
commented part showing an apperently valid conversion. I don't know at the
moment if the conversion within that comment for this -> C* counts as
explicit or implicit. 3.8/5 handles implicit conversions and static_cast.
Anyways, the text in the comment clearly seems to put the focus on
construction order issues, not implicit vs. explicit conversion issues.

Here the modified versions, hope now that shows the problem I can't solve.
If I have made again a mistake, post a corrected version that does show the
problem :-). First version assumes constructor of Derived has started
because A() was called, but as I said above, I couldn't find prove for that.
The comment in 12.7/2 has given me this impression.
In second version, constructor of Derived has definitely not finished, so
the lifetime of the object has not started yet.

struct A{};

struct Base
{
Base(Base*){}
};

struct Derived : A, Base
{
Derived() : A(), Base(this){}
};



struct Base
{
};

void foo(Base*);

struct Derived : Base
{
Derived() {foo(this);}
};



Thomas




---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: squell@alumina.nl (Marc Schoolderman)
Date: Thu, 27 Oct 2005 17:20:43 GMT
Raw View
Thomas Mang wrote:

(Note that I'm citing from the public draft.)

> Well, here two other versions. In case there's again something wrong with
> that snippet, pay attention to the example in 12.7/2, especially to the
> commented part showing an apperently valid conversion.

Yes, I was wrong in assuming that Deriv's constructor had not started.

> struct A{};
>
> struct Base
> { Base(Base*){} };
>
> struct Derived : A, Base
> { Derived() : A(), Base(this){} };

Without resorting to my assumption, this, like your original example, is
still undefined because of the conversion from Deriv->Base while Base is
not yet constructed. I think you will like the following example;

struct A {};

struct Base
{ Base(A*){} };

struct Deriv1 : A, Base {
     Deriv1() : A(), Base(this) { }   // OK
};

struct Deriv2 : Base, A {
     Deriv2() : A(), Base(this) { }   // undefined
};

In the first case, the pointer obtained still has subtly different
properties than an ordinary pointer (see 12.7/4).

The problem in this example, and your second one, is that an object
under construction is probably neither 'before lifetime has started' nor
'after lifetime has started', but somewhere in between. And indeed,
there is a Note in 3.8/3 which says: "12.6.2 and 12.7 describe the
behavior of objects during the construction and destruction phases.",
which seems to indicate that those sections trump 3.8.

However, it's just a Note.

Marc.

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: a9804814@unet.univie.ac.at ("Thomas Mang")
Date: Thu, 27 Oct 2005 18:50:39 GMT
Raw View

"Marc Schoolderman" <squell@alumina.nl> schrieb im Newsbeitrag
news:4360fb5a$0$756$5fc3050@dreader2.news.tiscali.nl...
> Thomas Mang wrote:
>
> (Note that I'm citing from the public draft.)
>
> > Well, here two other versions. In case there's again something wrong
with
> > that snippet, pay attention to the example in 12.7/2, especially to the
> > commented part showing an apperently valid conversion.
>
> Yes, I was wrong in assuming that Deriv's constructor had not started.


To be honest, I don't know if it has, or not.
As already said, I couldn't find the definition when _exactly_ the
constructor is assumed to have started.


>
> > struct A{};
> >
> > struct Base
> > { Base(Base*){} };
> >
> > struct Derived : A, Base
> > { Derived() : A(), Base(this){} };
>
> Without resorting to my assumption, this, like your original example, is
> still undefined because of the conversion from Deriv->Base while Base is
> not yet constructed.

According to 3.8, yes.
The big question is, what about 12.7/2. I have read the first sentence in
that para I don't know how many times, and I can't find anything violating
the requirements therein.



I think you will like the following example;
>
> struct A {};
>
> struct Base
> { Base(A*){} };
>
> struct Deriv1 : A, Base {
>      Deriv1() : A(), Base(this) { }   // OK
> };
> struct Deriv2 : Base, A {
>      Deriv2() : A(), Base(this) { }   // undefined
> };
>
> In the first case, the pointer obtained still has subtly different
> properties than an ordinary pointer (see 12.7/4).


Honestly, I don't understand the examples. Why should the first one be OK if
3.8/5 is taken into account (lifetime of Deriv1, the type this refers to,
has not begun!)?
12.7/4 is specifially only about typeid; what does the public draft handle
in 12.7/4?


>
> The problem in this example, and your second one, is that an object
> under construction is probably neither 'before lifetime has started' nor
> 'after lifetime has started', but somewhere in between.

Why somewhere in between? According to 3.8/1, lifetime starts after
constructor call has finished. Since the object is under construction, the
constructor call can't have finished, so it's lifetime has not begun, so it
would count to category 'before lifetime has started'.


And indeed,
> there is a Note in 3.8/3 which says: "12.6.2 and 12.7 describe the
> behavior of objects during the construction and destruction phases.",
> which seems to indicate that those sections trump 3.8.
>
> However, it's just a Note.


Yes, and also note it deals with the behavior and usage of objects itself,
not with converting pointers to the objects.


Thomas



---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: squell@alumina.nl (Marc Schoolderman)
Date: Fri, 28 Oct 2005 17:23:53 GMT
Raw View
Thomas Mang wrote:

>>struct A {};
>>
>>struct Base
>>{ Base(A*){} };
>>
>>struct Deriv1 : A, Base {
>>     Deriv1() : A(), Base(this) { }   // OK
>>};
>>struct Deriv2 : Base, A {
>>     Deriv2() : A(), Base(this) { }   // undefined
>>};
> Honestly, I don't understand the examples. Why should the first one be OK if
> 3.8/5 is taken into account (lifetime of Deriv1, the type this refers to,
> has not begun!)?

By 12.7 alone, the initializer of Base depends on a valid conversion
from Deriv to A, but in Deriv2, Base gets constructed before A does, so
the conversion is undefined.

Yes, I'm ignoring 3.8 here. No matter how hard you try, there's a
contradiction since it outlaws a whole range of uses. There's an open
issue which I think will tackle this in the future:

     http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#419

> 12.7/4 is specifially only about typeid; what does the public draft handle
> in 12.7/4?

The restrictions of calling virtual functions from constructors. It
contains an elaborate example dealing with explicitly passed pointers.

The draft is available from the WG's website, by the way.

>>there is a Note in 3.8/3 which says: "12.6.2 and 12.7 describe the
>>behavior of objects during the construction and destruction phases.",
>>which seems to indicate that those sections trump 3.8.
> Yes, and also note it deals with the behavior and usage of objects itself,
> not with converting pointers to the objects.

But 12.6,2 and 12.7 do.. :)

~Marc.

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: johnchx2@yahoo.com
Date: Fri, 28 Oct 2005 12:30:39 CST
Raw View
"Thomas Mang" wrote:
> 12.7/2 reads:
>
> "To explicitly or implicitly convert a pointer (an lvalue)
> referring to an object of class X to a pointer (reference)
> to a direct or indirect base class B of X, the construction
> of X and the construction of all of its direct or
> indirect bases that directly or indirectly derive from B
> shall have started and the destruction of these classes
> shall not have completed, otherwise the conversion results
> in undefined behavior."
>
> Therefore, this (nonsense) code snippet looks valid:
>
> struct Base
> {
> Base(Base*){}
> };
>
> struct Derived : Base
> {
> Derived() : Base(this){}
> };

<snip>

> And 3.8/5 says:
> "Before the lifetime of an object has started but after the storage which
> the object will occupy has been allocated
>  ....
> If the object will be or was of a non-POD class type, the program has
> undefined behavior if:
> ...
> - the pointer is implicitly converted (4.10) to a pointer to a base class
> type, or".
>
>
> So one para says it's OK, the other says it's undefined behavior. What am I
> missing / misinterpreting?


My impression is that the intent of 12.7/2 is to create a limited
exception to 3.8/5.  Otherwise, the paragraph would be meaningless
(since any program which is undefined under 12.7/2 would be undefined
under 3.8/5 already).  However, it would be nice if the paragraph said
this directly, and if 3.8/5 included a cross-reference.

(Actually, it looks like 12.7/2 could use some editing...what do those
parenthetics -- "(an lvalue)" and "(reference)" -- mean?  Should there
be "or"s in there?)

In 3.8/5, I'm not at all sure why only implicit conversions are
covered.  I can't imagine how the implicit-ness of a conversion could
possibly matter.  But perhaps my imagination fails me....

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "kanze" <kanze@gabi-soft.fr>
Date: Mon, 31 Oct 2005 22:33:44 CST
Raw View
Marc Schoolderman wrote:
> Thomas Mang wrote:

> >>struct A {};
> >>
> >>struct Base
> >>{ Base(A*){} };

> >>struct Deriv1 : A, Base {
> >>     Deriv1() : A(), Base(this) { }   // OK
> >>};
> >>struct Deriv2 : Base, A {
> >>     Deriv2() : A(), Base(this) { }   // undefined
> >>};

> > Honestly, I don't understand the examples. Why should the
> > first one be OK if 3.8/5 is taken into account (lifetime of
> > Deriv1, the type this refers to, has not begun!)?

> By 12.7 alone, the initializer of Base depends on a valid
> conversion from Deriv to A, but in Deriv2, Base gets
> constructed before A does, so the conversion is undefined.

> Yes, I'm ignoring 3.8 here. No matter how hard you try,
> there's a contradiction since it outlaws a whole range of
> uses. There's an open issue which I think will tackle this in
> the future:

>      http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#419

FWIW: the issue is perhaps less clear cut than my earlier
comments (cited in the referred open issue) might make it seem.
On one hand, I fear that code passing this as an initialization
parameter to either a base class or a member object is actually
quite common.  On the other hand, the following code actually
failed with Sun CC:

    class MyOStream
        : public virtual MyStreambuf
        , public std::ostream
        :   std::ostream( this )    //  implicite conversion to
                                    //  streambuf*, baseclass of
                                    //  MyStreambuf.
    {
    }

If I remember correctly, making the conversion explicit didn't
help any.  And the same code works with g++ (or worked with
whatever version of g++ I was using at the time, supposing I'm
not misremembering).

My first reaction was: this has to be crazy.  The compiler can
find the conversion in order to pass the correct this to the
constructor of MyStreambuf, but it can't find it to pass it as a
parameter.  On further thought, however: if someone later
derives from MyOStream, the position of MyStreambuf (and its
streambuf base class) relative to MyOStream will not be the
same.  And the only time the constructor of MyOStream will
really call the constructor of MyStreambuf is when it is the
most derived class.  (Of course, the constructor still has all
the information it needs.  It must know whether it is the most
derived class or not, and if it isn't, the most derived class
must have set up any pointers necessary for it to find the
MyStreambuf base class before calling it.)

--
James Kanze                                           GABI Software
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]