Topic: Calling pure virtual functions from an abstract class constructor


Author: richard_corden@hotmail.com (Richard Corden)
Date: Fri, 18 Nov 2005 17:37:28 GMT
Raw View
I'm trying to understand the scope of 10.4/6:

struct C1
{
   void void bar () = 0;
};

struct C2 : public C1
{
   C2 () : C1() {}
   void void bar ()
   {
   }
};

struct B : public C2
{
   B ();
   virtual void foo () = 0;
};

B::B ()
: C2()
{
   foo ();        // (1) undefined from 10.4/6
   bar ();        // (2) undefined?
}


My interpretation of 10.4/6 makes both of these undefined behaviour.
However, I do not see a justification for disallowing (2).  Am I
understanding something in the paragraph?


Regards,

Richard

--
Richard Corden

---
[ 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: Victor Bazarov <v.Abazarov@comAcast.net>
Date: Fri, 18 Nov 2005 14:55:40 CST
Raw View
Richard Corden wrote:
> I'm trying to understand the scope of 10.4/6:
>
> struct C1
> {
>   void void bar () = 0;

I am guessing you meant

     virtual void bar () = 0;

> };
>
> struct C2 : public C1
> {
>   C2 () : C1() {}
>   void void bar ()

And here one 'void' can safely be removed...

>   {
>   }
> };
>
> struct B : public C2
> {
>   B ();
>   virtual void foo () = 0;
> };
>
> B::B ()
> : C2()
> {
>   foo ();        // (1) undefined from 10.4/6
>   bar ();        // (2) undefined?
> }
>
>
> My interpretation of 10.4/6 makes both of these undefined behaviour.

That's wrong.  (2) is not a call to a virtual function for an object
"being constructed".  Since it resolves into 'C2::bar()', and the 'C2'
base class subobject has already been constructed at this time, the
behaviour is well-defined.

> However, I do not see a justification for disallowing (2).

It's perfectly fine.

 >  Am I
> understanding something in the paragraph?

I don't know how to answer that.

V

---
[ 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: Richard Corden <richard_corden@hotmail.com>
Date: Fri, 18 Nov 2005 19:16:08 CST
Raw View
Hi,

Victor Bazarov wrote:
> Richard Corden wrote:
>
>> I'm trying to understand the scope of 10.4/6:
>>
>> struct C1
>> {
>>   void void bar () = 0;
>
>
> I am guessing you meant
>
>     virtual void bar () = 0;

Yes, I did... :(

>> My interpretation of 10.4/6 makes both of these undefined behaviour.
>
>
> That's wrong.  (2) is not a call to a virtual function for an object
> "being constructed".  Since it resolves into 'C2::bar()', and the 'C2'
> base class subobject has already been constructed at this time, the
> behaviour is well-defined.

I believe my confusion is with what 'object' means in 10.4/6.  I
couldn't find a definition for 'object'.  One of the few places is 1.8/2
which actually says that an object can contain other objects, ie. sub
objects and base class subobjects.

I just wanted to make sure that there wasn't a legitimate implementation
reason why pure virtuals would require different handling.

I reckon that 10.4/6 could be rephrased using the kind of wording in
12.7/4, which IMHO is very clear.


Thanks for your reply.

Regards,

Richard



--
Richard Corden

---
[ 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: richard_corden@hotmail.com (Richard Corden)
Date: Mon, 21 Nov 2005 00:50:56 GMT
Raw View
Gene Bushuyev wrote:
> "Richard Corden" <richard_corden@hotmail.com> wrote in message
> news:437E779E.7010401@hotmail.com...
> [...]
>
> Your example doesn't reflect what the standard describes in 10.4/6

The motivation for my post was to determine if my example was covered by
10.4/6.  I am sorry that the original example was so full of typos that
its intent was not clearer.



>>I believe my confusion is with what 'object' means in 10.4/6.  I couldn't find
>>a definition for 'object'.  One of the few places is 1.8/2
>
>
> The definition of the object? - The one that is being constructed (destroyed.)
> The paragraph speaks about construction or destruction.

10.4/6 uses the phrase 'object being created'.  This text is also used
in 5.3.4/10 where it says the argument to 'operator new' shall be no
less than the size of the 'object being created'.  In that context this
would include base class subobjects.

[...]


>>I just wanted to make sure that there wasn't a legitimate implementation
>>reason why pure virtuals would require different handling.
>
>
> What do you mean by "different handling?" All the paragraph says is that calling
> a pure virtual function from an abstract base constructor (destructor) is
> undefined. Which is an obvious behavior as the dynamic type of the object is
> adjusted in the constructor to its static type, so calling a virtual function is
> always non-virtual.

Are you using an analogy here or does the standard explicitly specify
that?  For example should the call to 'foo' in the constructor of RHS
resolve to 'LHS::foo'?

struct Base
{
   Base () {}
   virtual void foo () = 0;
};

struct LHS : public virtual Base
{
   LHS ()
   {
   }

   virtual void foo ()
   {
     // call me?
   }
};

struct RHS : public virtual Base
{
   RHS ()
   {
     foo ();
   }
};


struct Derived : public LHS, RHS
{
   Derived ()
   : Base ()
   , LHS ()
   , RHS ()
   {
   }
};

int main ()
{
   Derived d;
}


> If such a function is abstract, the behavior is undefined
> (usually a crash).
> For example,
>
> struct A
> {
>     virtual void f() = 0;
>     A()
>     {
>         f(); // undefined 10.4/6
>     }
> };
>
> struct B : A
> {
>     virtual void f() {}
>     B() : A
>     {
>         f(); // this is ok
>     }
> }
>

'B' in this example is not abstract and so 10.4/6 would not apply
anyway.  Using your example as a template, let me try my original
example again:

struct A
{
   virtual void f() = 0;
   virtual void b() = 0;
};

struct B : A
{
   virtual void f() {}
   B() : A()
   {
     f(); // (1)
     b(); // (2)
   }
}

IMHO, (1) is identical to (2) with respect 10.4/6.

I have come up with two possible interpretations of 10.6/4:

a) The standard does wish to forbid both (1) and (2), ie. 'In the
constructor or destructor for an abstract class, calling a pure virtual
function that is a member of the class or a base class is undefined'.

b) The standard wants to be more flexible, ie. 'In the constructor or
destructor for an abstract class, calling a pure virtual function whose
final overrider is still pure virtual is undefined'.

If I understand you correctly, I believe you feel the standard implies
option 'b'.

Option 'b' is certainly less restrictive, however, I have come to learn
that very little goes into the standard without a lot of thought, and so
it would not surprise me if some implementation detail makes it
difficult for 'option b' to be the general rule for all compiler vendors.


Thanks again,

Regards,

Richard


--
Richard Corden

---
[ 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: Richard Corden <richard_corden@hotmail.com>
Date: Mon, 21 Nov 2005 09:28:09 CST
Raw View
Alf P. Steinbach wrote:
> * Richard Corden:
>
>>struct A
>>{
>>   virtual void f() = 0;
>>   virtual void b() = 0;
>>};
>>
>>struct B : A
>>{
>>   virtual void f() {}
>>   B() : A()
>>   {
>>     f(); // (1)
>>     b(); // (2)
>>   }
>>}

[...]

> In your example (2) is not pure virtual.
>

[In your example (1) is not pure virtual.]


I have not been able to find any text which says that if 'f' has a non
pure virtual final overrider it ceases to be 'pure virtual'.  The only
section I found was 10.4/4 which has:

"A class is abstract if it contains or inherits at least one pure
virtual for which the final overrider is pure virtual."

But IMHO, this implies that 'f' will still be a pure virtual function,
even if it has a non pure virtual overrider.  However, the class will
then not be abstract.


Thanks!


Regards,

Richard




--
Richard Corden

---
[ 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: Richard Corden <richard_corden@hotmail.com>
Date: Mon, 21 Nov 2005 09:27:43 CST
Raw View


kuyper@wizard.net wrote:
> Richard Corden wrote:
>
>>Gene Bushuyev wrote:
>
> .
>
>>>The definition of the object? - The one that is being constructed (destroyed.)
>>>The paragraph speaks about construction or destruction.
>>
>>10.4/6 uses the phrase 'object being created'.  This text is also used
>>in 5.3.4/10 where it says the argument to 'operator new' shall be no
>>less than the size of the 'object being created'.  In that context this
>>would include base class subobjects.
>
>
> No - not in the sense that you mean. Inside the constructor of a
> derived class, construction of the base class is already complete; it's
> only the derived object which is still being constructed. That being
> the case, calling a base class pure virtual function which is defined
> in the derived class is perfect legal; it calls the version defined for
> that derived class.

I agree that this is a nice interpretation.  I disagree that the words
unambiguously say this.  IMHO, the standard uses 'object being created'
in several at least one other contexts with a different meaning, then
all of us can legitimately chose any interpretation we wish.


>>>>I just wanted to make sure that there wasn't a legitimate implementation
>>>>reason why pure virtuals would require different handling.
>>>
>>>
>>>What do you mean by "different handling?" All the paragraph says is that calling
>>>a pure virtual function from an abstract base constructor (destructor) is
>>>undefined. Which is an obvious behavior as the dynamic type of the object is
>>>adjusted in the constructor to its static type, so calling a virtual function is
>>>always non-virtual.
>>
>>Are you using an analogy here or does the standard explicitly specify
>>that?
>
>
> Yes, the standard is pretty specific about when precisely an object
> becomes an object of the derived type, and it's just as specific about
> when it ceases to become an object of that type. This corresponds to
> the point at which, in a typical implementation, the virtual function
> table pointer is changed.

I don't see how this answers my question?  I was asking about 'adjusted
in the constructor to its static type'.

>
>>struct A
>>{
>>   virtual void f() = 0;
>>   virtual void b() = 0;
>>};
>>
>>struct B : A
>>{
>>   virtual void f() {}
>>   B() : A()
>>   {
>>     f(); // (1)
>>     b(); // (2)
>>   }
>>}
>>
>>IMHO, (1) is identical to (2) with respect 10.4/6.
>
>
> No. During the construction of B, the object is of type B, and the
> virtual call to f() results in a call to B::f(). A definition of B::b()
> might be provided in a derived class, but that doesn't help, because at
> the time of the call to the constructor of B, the object isn't yet of
> the derived class. Therefore the only definition of b() that could be
> used is one provided in B or it's base classes - and there is no such
> definition.

I cannot see anywhere in 10.4/6 where it talks about the function being
defined or not.  In another post Alf, has mentioned that (1) is not
actually pure, this may be the reason that 10.4/6 doesn't apply.


Regards,

Richard


--
Richard Corden

---
[ 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: richard_corden@hotmail.com (Richard Corden)
Date: Wed, 23 Nov 2005 14:44:16 GMT
Raw View
johnchx2@yahoo.com wrote:
> Richard Corden wrote:
>
>>Alf P. Steinbach wrote:
>>
>>[In your example (1) is not pure virtual.]
>>
>>
>>I have not been able to find any text which says that if 'f' has a non
>>pure virtual final overrider it ceases to be 'pure virtual'.
>
>
> I think the key is that there is no function 'f()'.  There is A::f()
> and B::f().  A::f() is, and ever shall be, pure virtual.  B::f() is not
> now, nor ever has been, pure virtual.
>
> The member name lookup rules (*not* virtual function dispatch) specify
> that the expression f() in B::B() resolves to B::f().

Excellent!!   This is the section I am missing.

However, I was unable to find where this is stated, can you show me
where this is stated?

Cheers,

Richard

>
> The same member name lookup rules say that b() in B::B() resolves to
> A::b(), which is pure virtual, making the effect of the call undefined.
>
> ---
> [ 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                       ]
>


--
Richard Corden

---
[ 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                       ]