Bo said:
You shouldn't always give people what they believe they want.
Calling a virtual function on a non-existing object is one of those
things. Here C++ avoids undefined behavior by defining that you can
only call the function for the object that actually exists at that
point.
Are we being completely honest here referring to the object as
"non-existing"? Under the current rules it does not exist but we are
discussing the quality of this very rules, aren't we? Under the proposed
(and time-tested) changed rules it would be existing, just not fully
initialized. Calling functions (virtual or not) on a
not-fully-initialized object is nothing new: a member function called
from the constructor is called on a not-fully-initialized object. What
is a point of discussing the conceptual correctness of letting people
call functions on a not-fully-initialized object when they already can
do it?
My impression is that C++ tries to avoid the shoot-in-the-foot
Not to avoid, but make it harder:
"In C++ it's harder to shoot yourself in the foot, but when you do, you
blow off your whole leg." — Bjarne Stroustrup.
It did not mean to make legitimate work harder, though. And the ability
to express useful ideas always comes at cost of danger. Remember
standard Pascal? It was clearly as safe as a language can get; so safe
that it was impossible to write anything useful in it (useful varieties
like Turbo Pascal introduced external modules, so they became useful but
lost the ability to statically proof the correctness). Certainly it is a
matter of personal choice where to draw the line and that's to try to
throw in some objectivity I refer to Java: From both my experience and
the prevalent opinion, Java is much safer language than C++ (and
respectively slightly less universal and capable). Therefore a litmus
test to me is: if something is safe enough for allowing it in Java, not
including that same capability in C++ for safety reasons is probably not
a good idea.
situation whenever possible (and without a run-time cost
. This
does leave some opportunities for blowing a leg off, for the
adventurous programmer.
For some definition of work. I'm no Java expert, but from what I
understand the cost you want to avoid in 2) now moves to a test for a
fully constructed object in every call to the function.
Nah, it just lets you slip (Java!); see
http://en.wikipedia.org/wiki/Virtual_function#Java_2.
BTW, I just found the reference to this article of Scott Meyers ibid in
Wikipedia:
http://www.artima.com/cppsource/nevercall.html. I think
logically those who agree with this his opinion should agree to us
(Howard and myself) as well: what a point in having a "safety feature"
that is recommended to be used .. never?
I say "logically" because Scott himself would apparently disagree:
"... That's how every part of C++ will treat it, and the treatment makes
sense: the BuyTransaction-specific parts of the object haven't been
initialized yet, so it's safest to treat them as if they didn't exist".
I, on the other hand, cannot see a big difference between calling any
function within a constructor (where some parts of an object may not
have been initialized yet) and calling a virtual function ibid. I
believe that treating the derived-class object as "non-existing" before
entering its construction function is an arbitrary and not very useful
choice of the Standard.
My point is that the derived object should fully construct itself, and
not depend on the base object calling some function during its
construction. It is about the distribution of responsibilities.
See, if only purpose why constructors are used were the initialization
of the parts of the constructor's specific class I could agree to that.
In practice, however, there are other tasks that are hooked to the
constructors (because there is no other hooks in C++ for doing that in
general case). For example (the list is certainly incomplete):
1. To track creation of all objects in hierarchy where class-specific
information is necessary for tracking.
2. To complete the initialization of parallel bases.
3. To Register all created objects from hierarchy somewhere where the
registration data, registrar or registration algorithm is
derived-class-specific (that is, specific to static members of the
derived class or its state-independent behavior).
If C++ allowed an easy and inexpensive way of hooking in these (I am
aware of a wrapper/handler-based method but do not consider it easy
enough of inexpensive), a lion share of the current needs may have been
eliminated (I am not sure about all of them). As it is now, I see Java
behavior more beneficial.
-Pavel