There are two possible interpretations of this:
1) the function called is the final overrider (according to 5.2.2/1)
where the object under construction or destruction is considered to be
a most derived object that has the type of the constructor or
destructor�s class (similarly to dynamic_cast);
2) an implementation is free to call any version of the virtual
function defined in the constructor or destructor�s own class or in
one of its bases.
Consider the following example:
#include <iostream>
struct B
{
virtual void f() { std::cout << "B::f" << std::endl; }
};
struct D
{
D() { f(); }
virtual void f() { std::cout << "D::f" << std::endl; }
};
int main()
{
D d;
}
According to the first interpretation, an implementation shall choose
D::f for the call.
According to the second interpretation, it is unspecified whether D::f
or B::f is actually called.
Which one interpretation is intended?
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
C++03 - 12.7/3: "When a virtual function is called directly or
indirectly from a constructor (including from the mem-initializer for
a data member) or from a destructor, and the object to which the call
applies is the object under construction or destruction, the function
called is the one defined in the constructor or destructor�s own class
or in one of its bases, but not a function overriding it in a class
derived from the constructor or destructor�s class, or overriding it
in one of the other base classes of the most derived object (1.8)."
There are two possible interpretations:
1) the function called is the final overrider (according to 5.2.2/1)
where the object under construction or destruction is considered to be
a most derived object that has the type of the constructor or
destructor�s class (similarly to dynamic_cast);
2) an implementation is free to call any version of the virtual
function defined in the constructor or destructor�s own class or in
one of its bases.
Consider the following example:
#include <iostream>
struct B
{
virtual void f() { std::cout << "B::f" << std::endl; }
};
struct D : B
> [Note for the moderator: This is edited version (I have fixed erratum
> in the example: D shall derive from B)]
>
> C++03 - 12.7/3: "When a virtual function is called directly or
> indirectly from a constructor (including from the mem-initializer for
> a data member) or from a destructor, and the object to which the call
> applies is the object under construction or destruction, the function
> called is the one defined in the constructor or destructor�s own class
> or in one of its bases, but not a function overriding it in a class
> derived from the constructor or destructor�s class, or overriding it
> in one of the other base classes of the most derived object (1.8)."
>
> There are two possible interpretations:
>
> 1) the function called is the final overrider (according to 5.2.2/1)
> where the object under construction or destruction is considered to be
> a most derived object that has the type of the constructor or
> destructor�s class (similarly to dynamic_cast);
>
> 2) an implementation is free to call any version of the virtual
> function defined in the constructor or destructor�s own class or in
> one of its bases.
>
Just found a third interpretation:
3) an implementation always calls the statically chosen function:
struct B
{
virtual void f() { std::cout << "B::f" << std::endl; }
void g() { f(); }
};
struct D : B
{
D() { g(); }
virtual void f() { std::cout << "D::f" << std::endl; }
};
int main()
{
D d;
}
According to the third interpretation, it will print "B::f".
The first.
The intention is/was that in a T constructor, the object under
construction is effectively of most derived type T.
I can talk with very high degree of confidence about the intention
because this is a matter of very simple logic and practicality and
what it could at all be /for/, -- not because I was present in those
discussions, I wasn't.
Cheers & hth.,
- Alf
The first. Virtual dispatch goes to the most-derived function. It
happens that during construction or destruction, that is the most-
derived that exists in the object at that time. Implementations do
not have flexibility in which function they call.
-- James
> C++03 - 12.7/3: "When a virtual function is called directly or
> indirectly from a constructor (including from the mem-initializer for
> a data member) or from a destructor, and the object to which the call
> applies is the object under construction or destruction, the function
> called is the one defined in the constructor or destructor?s own class
> or in one of its bases, but not a function overriding it in a class
> derived from the constructor or destructor?s class, or overriding it
> in one of the other base classes of the most derived object (1.8)."
>
> There are two possible interpretations of this:
>
> 1) the function called is the final overrider (according to 5.2.2/1)
> where the object under construction or destruction is considered to be
> a most derived object that has the type of the constructor or
> destructor?s class (similarly to dynamic_cast);
>
> 2) an implementation is free to call any version of the virtual
> function defined in the constructor or destructor?s own class or in
> one of its bases.
>
> Consider the following example:
>
> #include <iostream>
>
> struct B
> {
> virtual void f() { std::cout << "B::f" << std::endl; }
> };
>
> struct D
> {
> D() { f(); }
> virtual void f() { std::cout << "D::f" << std::endl; }
> };
>
> int main()
> {
> D d;
> }
>
> According to the first interpretation, an implementation shall choose
> D::f for the call.
> According to the second interpretation, it is unspecified whether D::f
> or B::f is actually called.
>
> Which one interpretation is intended?
To my knowledge, the intended interpretation is #1 and that is (to my
knowledge) also the one that is implemented in all current compilers.
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
This.
When inside the constructor, or in something called from the
constructor, the object's dynamic type is the same as the static type
of the class containing the constructor. Option 1 is closest to that.
Bo Persson
> or in one of its bases, but not a function overriding it in a class
This ``or'' doesn't mean that there is a lottery to be held between
the constructor's class and bases regarding whose virtual gets
called.
It simply means that the virtual function might not actualy be defined in
the
constructor or destructor's class; the call could go to a virtual which is
inherited from a base.
Sometimes ``or'' means there is a sequenced choice (like the || operator).
Call the one defined in its own class || call one defined in a base.
:)
> 2) an implementation is free to call any version of the virtual
> function defined in the constructor or destructor?s own class or in
> one of its bases.
That's insane. It would be like, say, allowing function arguments
to be evaluated in any order. Errr, wait a minute ... :)
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std...@netlab.cs.rpi.edu<std-c%2B%2...@netlab.cs.rpi.edu>
* When a virtual function specified by unqualified-id is called for an
object under construction _or destruction_ and the call occurs during
execution of:
-- body of the object's constructor
-- initialization of the object's non-static data member
-- body of the object's destructor
-- destructor of the object's non-static data member
the function called is the final overrider in the object's class, but
not a function overriding it in a class derived from the object's
class, or overriding it in one of the other base classes of the most
derived object (1.8).
Note: this also covers the case when the destructor for a non-static
data member is called from the object's constructor due to throwing an
exception.
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@netlab.cs.rpi.edu]
I think, the presence of two interpretations of "most derived type"
and two interpretations of "most derived object" makes the rules
inconsistent. For example,
"the function called is the one defined in the constructor or
destructor's own class or in one of its bases, but not a function
overriding it in a class derived from the constructor or destructor's
class, or overriding it in one of the other base classes of the most
derived object (1.8)." (see 12.7/3)
Here "most derived object" means the most enclosing object which is
not subobject of any other object.
"if the operand of the dynamic_cast refers to the object under
construction or destruction, this object is considered to be a most
derived object that has the type of the constructor or destructor's
class" (see 12.7/5)
Here "most derived object" actually means the most base object under
construction (if the base class subobject is under construction then
any derived object (which contains that subobject) is apparently under
construction too).
"When a virtual function is called directly or indirectly from a
constructor (including from the mem-initializer for a data member) or
from a destructor, and the object to which the call applies is the
object under construction or destruction [...]". (see 12.7/3)
The constructor's execution may include initialization of base class
subobjects and non-static data members (regardless of whether the
corresponding mem-initializer is specified or the initialization
performs implicitly). The hint "including from the mem-initializer for
a data member" does not explicitly exclude initialization of base
class subobjects and initialization of data members which occurs
outside of mem-initializer. Similarly, the destructor's execution may
include calls of destructors for base class subobjects and non-static
data members.
#include <iostream>
struct D *d;
struct A
{
A();
~A();
};
struct B
{
B(D *d) { ::d = d; }
virtual void f() { std::cout << "B::f" << std::endl; }
};
struct D : B
{
D() : B(this) {}
virtual void f() { std::cout << "D::f" << std::endl; }
A a;
};
// most derived
struct M : D
{
virtual void f() { std::cout << "M::f" << std::endl; }
};
A::A() { d->f(); } // #1
A::~A() { d->f(); } // #2
int main() { M(); }
It's not clear enough which f can be called at #1 and #2 in the
example above.
I think, the rules shall be reformulated, and I hope, my wording below
is not so bad:
When a virtual function specified by unqualified-id is called for an
object under construction and the call occurs during execution of:
-- body of the object's constructor
-- initialization of the object's non-static data member
-- body of the object's destructor
-- destructor of the object's non-static data member
the function called is the final overrider in the object's class, but
not a function overriding it in a class derived from the object's
class, or overriding it in one of the other base classes of the most
derived object (1.8).
--