Topic: Why allow virtual function calls in constr
Author: verec@micronet.fr (Jean-Fran\gois Brouillet)
Date: 1995/11/07 Raw View
In article <9511021143.AA01851@slsv7xt.lts.sel.alcatel.de>, kanze
<kanze@lts.sel.alcatel.de> wrote:
>In article <1995Oct31.171443.3987@oma.com> rmartin@oma.com (Robert
>Martin) writes:
>|> harald@bion.kth.se (Harald Winroth) writes:
>|> >There is a common pattern in which a base class implements an algorithm but
>
>|> >leaves some of the steps unspecified (in C++ typically as pure virtual
>|> >functions). These steps are later defined in subclasses. The current
>|> >standard draft prevents this pattern to be used in constructors, which I
>|> >think is unfortunate.
>
>|> I agree that the current rules for deploying virtual functions in
>|> constructors and destructors can be inconvenient at times. But they
>|> have a distinct advantage. The rules, written as they are, guarantee
>|> that no member function of a class (other than one of its
>|> constructors) will be called until all the members and bases of that
>|> class have been initialized.
>Actually, the rules make this guarantee even for constructors and
>destructors. The only code in class X that can be executed before all
>of the subobjects are initialized is the initialization lists of
>constructors; off hand, I don't believe that (non-static) member
>functions can be called in initialization lists. (I'm too lazy to
>check, and since it isn't something I'd want to do anyway.)
>
>IMHO, this is a *very* important guarantee, well worth the occasional
>inconvenience.
>
>|> If the rules allowed virtual deployment in constructors or
>|> destructors, then functions could be called in classes where the
>|> members and/or bases had not been initialized, or had been destructed.
>|> And this, IMHO, would be untennable.
>
>Also, these functions would presumably not have anyway of knowing that
>the subobjects were only raw memory. So they would probably use
>functions of the subobjects, with the result that everyday member
>functions end up getting called on raw memory. Not exactly what I
>would call a healthy state of affairs.
>--
>
>James Kanze Tel.: (+33) 88 14 49 00 email: kanze@gabi-soft.fr
>GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
>Conseils en informatique industrielle --
> -- Beratung in industrieller Datenverarbeitung
Hmmm! The question doesn't seem to be why virtual functions calls are not
allowed in ctor/dtors, but why there isn't any kind of sepcial virtual
functions which *could* be allowed to be called in such a context.
Standard, "ordinary" virtual function would behave as usual, but a special
form of them, introcudced with an apropriate keyword would tell the
compiler
"Hey, I know what I'm doing, and I take personal responsability for not
using any field not defined in the first base-class which declared me".
What about using a "constructor" keyword, meaning both "virtual" and "can
be called by ctors/dtors in any derived class" ?
Jean-Fran ois Brouillet
Macintosh Software Developer
verec@micronet.fr (preferred)
verecundus@eworld.com
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: wil@ittpub.nl (Wil Evers)
Date: 1995/11/10 Raw View
In article <199511071556.QAA12601@logatome.micronet.fr> verec@micronet.fr
(Jean-Fran\gois Brouillet) writes:
> Hmmm! The question doesn't seem to be why virtual functions calls are
> not allowed in ctor/dtors, but why there isn't any kind of sepcial
> virtual functions which *could* be allowed to be called in such a
> context.
>
> Standard, "ordinary" virtual function would behave as usual, but a
> special form of them, introcudced with an apropriate keyword would tell
> the compiler "Hey, I know what I'm doing, and I take personal
> responsability for not using any field not defined in the first
> base-class which declared me".
>
> What about using a "constructor" keyword, meaning both "virtual" and
> "can be called by ctors/dtors in any derived class" ?
If the member function to be called does not use any field not defined in
the first base-class which declared it, it can - and should - be defined
in that base class. If you have a set of such member functions from which
one is to be selected depending on the derived type of the object under
construction, you can pass a pointer to such a member function from the
derived constructor's initializer list to one of the base class
constructors:
class Base {
protected :
void calledForDerived1();
void calledForDerived2();
Base(void (Base::*pf)()) { pf(); }
};
class Derived1 : public Base {
public:
Derived1() : Base(calledForDerived1) { }
};
class Derived2 : public Base {
public :
Derived2() : Base(calledForDerived2) { }
};
I don't particularly like this because I distrust constructors whose
functionality is tightly coupled to a derived class. But if you really
need such a thing, this technique is certainly less confusing than
introducing yet another keyword complicating the language rules for
virtual function call resolution.
- Wil
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: kanze <kanze@lts.sel.alcatel.de>
Date: 1995/11/02 Raw View
In article <1995Oct31.171443.3987@oma.com> rmartin@oma.com (Robert
Martin) writes:
|> harald@bion.kth.se (Harald Winroth) writes:
|> >There is a common pattern in which a base class implements an algorithm but
|> >leaves some of the steps unspecified (in C++ typically as pure virtual
|> >functions). These steps are later defined in subclasses. The current
|> >standard draft prevents this pattern to be used in constructors, which I
|> >think is unfortunate.
|> I agree that the current rules for deploying virtual functions in
|> constructors and destructors can be inconvenient at times. But they
|> have a distinct advantage. The rules, written as they are, guarantee
|> that no member function of a class (other than one of its
|> constructors) will be called until all the members and bases of that
|> class have been initialized.
Actually, the rules make this guarantee even for constructors and
destructors. The only code in class X that can be executed before all
of the subobjects are initialized is the initialization lists of
constructors; off hand, I don't believe that (non-static) member
functions can be called in initialization lists. (I'm too lazy to
check, and since it isn't something I'd want to do anyway.)
IMHO, this is a *very* important guarantee, well worth the occasional
inconvenience.
|> If the rules allowed virtual deployment in constructors or
|> destructors, then functions could be called in classes where the
|> members and/or bases had not been initialized, or had been destructed.
|> And this, IMHO, would be untennable.
Also, these functions would presumably not have anyway of knowing that
the subobjects were only raw memory. So they would probably use
functions of the subobjects, with the result that everyday member
functions end up getting called on raw memory. Not exactly what I
would call a healthy state of affairs.
--
James Kanze Tel.: (+33) 88 14 49 00 email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: law@solution-frameworks.com
Date: 1995/11/03 Raw View
In <1995Oct31.171443.3987@oma.com>, rmartin@oma.com (Robert Martin) writes:
>If the rules allowed virtual deployment in constructors or
>destructors, then functions could be called in classes where the
>members and/or bases had not been initialized, or had been destructed.
>And this, IMHO, would be untennable.
Mr. Martin,
This would not be "untennable" if the derived class functions that
could be called were limited to static member functions, as Mr. Winroth
proposed. I would appreciate your insights into this matter once you
have taken that detail into account. Thank you.
Bill Law
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: law@solution-frameworks.com
Date: 1995/11/03 Raw View
In <9511021143.AA01851@slsv7xt.lts.sel.alcatel.de>,
kanze <kanze@lts.sel.alcatel.de> writes:
>Also, these functions would presumably not have anyway of knowing that
>the subobjects were only raw memory. So they would probably use
>functions of the subobjects, with the result that everyday member
>functions end up getting called on raw memory. Not exactly what I
>would call a healthy state of affairs.
Mr. Kanze,
Please note that when subject to the limitation of being "static virtual"
functions, as Mr. Winroth alluded to in his posting to which Mr. Martin was
replying, "these functions" would not have any way of calling (non-static)
member functions, everyday ones or otherwise, and therefore couldn't call
them "on raw memory." I am interested in what your thoughts on this topic
might be in light of this additional information. Thank you.
Bill Law
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: law@solution-frameworks.com
Date: 1995/11/06 Raw View
In <47e529$ha8@news.vcd.hp.com>, Marco Dalla Gasperina
<Marco_Dalla-Gasperina@HP-Vancouver-om1.om.hp.com> writes:
>If you restrict yourself to "static virtual" functions, you are restricted
>to not calling any functions at all. The construct is an oxymoron.
We're talking about the C++ standard here, so the whole thing is a
figment of your imagination. Imagine "static virtual" functions, if you
can. They're no more oxymoronic than, say, member function templates.
Surely you're not suggesting the concept is self-contradictory?
Bill
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1995/10/26 Raw View
In article j45@manuel.anu.edu.au, keagchon@mehta.anu.edu.au (Geoff Keating 9205156) writes:
>
>The draft WP says that [section class.cdtor:3]
>
> When a virtual function is called directly or indirectly from a con-
> structor (including from its ctor-initializer) or from a destructor,
> and the object to which the call applies is the object under construc-
> tion 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 complete object (_intro.object_).
>
>Why allow this, rather than refusing calls to virtual functions at
>all? It would be nice to say that `if I override this function, then
>the old version will _never_ be called on objects of this type'.
This language design decision is explained in the ARM, among other places.
Given
class base { ... };
class derived : public base { ... };
suppose we constuct an object of type derived.
The first thing that happens is that the base constructor runs. While that
constructor is running, the object has type base, not type derived. Hence,
any virtual functions that are called will never be those declared in
derived, because the object at this moment is not a derived object; it is
a base object. That is (part of) the C++ object model.
In other words, when a constructor runs, the object has exactly the type
of the constructor's class.
The same is true of destructors. When the body of the derived destructor
completes, the destructor for base runs. At that point, the object has
ceased being a derived (the derived part has been destroyed), and is now
a base object.
Other language designs are possible. For example, we could say that when
you construct a derived, the object is a derived even before the derived
constructor begins to run, and you get the derived virtual functions when
called from the base constructor or destructor. Sometimes that will be the
effect you want. But in the general case, member functions of derived
could be called which depend on the derived constructor having already
run, and on the derived destructor not yet having run. This will not always
be the case. We would need additional rules, such as
1. You cannot call a virtual function from a ctor/dtor, or
2. You have to write a virtual function so that it doesn't matter whether
the ctor or dtor for its type has run or not, or
3. If you call a virtual function from a ctor/dtor, the results are undefined.
With this object model, you cannot define the semantics of a virtual
function call from a ctor/dtor unless you somehow enforce option 2 above.
Example:
class base {
public:
virtual int f() { return 0; }
...
};
class derived : public base {
public:
virtual int f();
derived(const char* name) { str = new ifstream(name); }
~derived() { delete str; }
private:
ifstream* str;
};
int derived::f() {
int i;
*str >> i; // fails before ctor runs and after dtor runs
return i;
}
If a constructor or destructor for base calls f() and gets derived::f(),
you cannot define any semantics for the result. Hence my three options above.
This seems to me to be inferior to the actual C++ semantics.
Another alternative, used in some languages, is that the base ctors
and dtors are not run automatically. The derived class ctor/dtor specifies
the complete order of execution. This gives you the control you need to
let the above example work, but it also means, it seems to me, that the
derived class must know all about all its base classes. You lose the
modularity of needing to know only the interface of the base class. Any
change in any base class might require that you rewrite ctors and dtors
of derived classes. Some people think this model still provides an overall
advantage. I don't agree, but in any case, it isn't C++.
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1995/10/29 Raw View
fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>clamage@Eng.Sun.COM (Steve Clamage) writes:
>>Other language designs are possible. For example, we could say that when
>>you construct a derived, the object is a derived even before the derived
>>constructor begins to run, and you get the derived virtual functions when
>>called from the base constructor or destructor. Sometimes that will be the
>>effect you want.
>Yes. In fact I think that would *often* be the effect you want.
I disagree, and it would be a dangerous language rule, as I argue below.
>>But in the general case, member functions of derived
>>could be called which depend on the derived constructor having already
>>run, and on the derived destructor not yet having run. This will not always
>>be the case. We would need additional rules, such as
>>1. You cannot call a virtual function from a ctor/dtor, or
>>2. You have to write a virtual function so that it doesn't matter whether
>> the ctor or dtor for its type has run or not, or
>>3. If you call a virtual function from a ctor/dtor, the results are undefined.
>You are overstating the case here. Option 2 is far more sweeping than
>it needs to be. An alternative is a more restricted version of option 2
>that only applies to those virtual functions which are called from
>base class ctor/dtors:
>2b. If a virtual function is called from a base class ctor or dtor, then
> you have to write it so that it doesn't matter whether the ctor or dtor
> for its class has run or not.
But how do you know, given a set of library classes supplied in binary
form, what virtual functions are called from which constructors and
destructors? When I write a class, how do I know what classes will
someday be derived from it, and what future virtual functions might be
safe to call from my class's constructors and destructor?
Suppose the class library I acquire describes the virtual functions
called from each ctor/dtor. I make a check list to be sure that
any overriding virtual functions are safe to call from the base
class. Someone else now derives from my class, and has to combine
the data from all the base classes to figure out which new
overrides might be called from some base class ctor/dtor.
Now version 2 of the library class is released. Implementation
details of some constructors and destructors have changed. I have
to revise my checklist of virtual functions (and so do users
of my classes) to be sure any virtual functions that were not
previously called, but are called now, are safe.
You LIKE this as the required design procedure? That every class
must know the implementation details of all the constructors and
destructors of each of its base classes?
>If this enforcement was found to be difficult, then perhaps
>such virtual functions could be identified with a particular
>keyword (`raw'?), so that the compiler could then enforce it:
>only functions declared `raw' could be called from a constructor or
>destructor.
I think you would have to add this feature, or else disallow
virtual calls from constructors and destructors.
--
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: vandevod@cs.rpi.edu (David Vandevoorde)
Date: 1995/10/30 Raw View
>>>>> "SC" == Steve Clamage <clamage@Eng.Sun.COM> writes:
[...]
SC> But how do you know, given a set of library classes supplied in binary
SC> form, what virtual functions are called from which constructors and
SC> destructors? When I write a class, how do I know what classes will
SC> someday be derived from it, and what future virtual functions might be
SC> safe to call from my class's constructors and destructor?
True, but that is always the case with virtual functions. If you don't
want overriding outside your control, there are other options for that
(e.g., a virtual call to a private hierarchy of helper classes). If you
don't want overriding at all; you can always fully qualify the call.
SC> Suppose the class library I acquire describes the virtual functions
SC> called from each ctor/dtor. I make a check list to be sure that
SC> any overriding virtual functions are safe to call from the base
SC> class. Someone else now derives from my class, and has to combine
SC> the data from all the base classes to figure out which new
SC> overrides might be called from some base class ctor/dtor.
[...]
It would probably be a poorly designed library. Most reasonable
techniques I can imagine with virtual calls from a constructor would be
that the call resolves to the full object which is then reponsible
to relay-call the same function its base components.
What was the argumentation that lead to give defined semantics to
calls to virtual functions in constructors?
[...]
SC> I think you would have to add this feature, or else disallow
SC> virtual calls from constructors and destructors.
Right now, I think it makes sense disallowing them.
Daveed
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: harald@bion.kth.se (Harald Winroth)
Date: 1995/10/30 Raw View
In article <470gvp$i3h@engnews1.eng.sun.com> clamage@Eng.Sun.COM (Steve Clamage) writes:
> [discussion about calling virtual function in derived classes from base class
> constructor deleted]
>
> >fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
> >[...]
> >Yes. In fact I think that would *often* be the effect you want.
>
> I disagree, and it would be a dangerous language rule, as I argue below.
There is a common pattern in which a base class implements an algorithm but
leaves some of the steps unspecified (in C++ typically as pure virtual
functions). These steps are later defined in subclasses. The current
standard draft prevents this pattern to be used in constructors, which I
think is unfortunate.
The main problem with calling virtual functions in constructors is that the
complete object hasn't been fully initialized at the point of the call.
However, isn't that a logical problem only if you really have access to
the (sub-)object in that function? What if there was a type of member
function with access only to static members and no implicit object pointer
(just like an ordinary static member function), but which could be called
through the virtual function table? Something like:
class B {
int i;
public:
static virtual int init() { return 1; } // not C++, but what if?
B() : i(init()) {}
};
class D : public B {
public:
static virtual int init() { return 2; }
};
B b; // B::B calls B::init which sets b.i to 1
D d; // B::B calls D::init which sets d.i to 2
Of course, the algorithms "return 1" and "return 2" here are so simple that
you would write them directly in the colon list of B::B and D::D, but
for more complicated code this would be convenient.
---
Harald Winroth
Computational Vision and Active Perception Laboratory,
Royal Institute of Technology, Stockholm.
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: rmartin@oma.com (Robert Martin)
Date: 1995/10/31 Raw View
harald@bion.kth.se (Harald Winroth) writes:
>There is a common pattern in which a base class implements an algorithm but
>leaves some of the steps unspecified (in C++ typically as pure virtual
>functions). These steps are later defined in subclasses. The current
>standard draft prevents this pattern to be used in constructors, which I
>think is unfortunate.
I agree that the current rules for deploying virtual functions in
constructors and destructors can be inconvenient at times. But they
have a distinct advantage. The rules, written as they are, guarantee
that no member function of a class (other than one of its
constructors) will be called until all the members and bases of that
class have been initialized.
If the rules allowed virtual deployment in constructors or
destructors, then functions could be called in classes where the
members and/or bases had not been initialized, or had been destructed.
And this, IMHO, would be untennable.
--
Robert Martin | Design Consulting | Training courses offered:
Object Mentor Assoc.| rmartin@oma.com | OOA/D, C++, Advanced OO
2080 Cranbrook Rd. | Tel: (708) 918-1004 | Mgt. Overview of OOT
Green Oaks IL 60048 | Fax: (708) 918-1023 | Development Contracts.
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]