Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!usc!zaphod.mps.ohio-state.edu!mips!daver!tscs!tct!chip From: chip@tct.uucp (Chip Salzenberg) Newsgroups: comp.std.c++ Subject: Re: overloading vs. virtual functions Summary: Well, don't do that, then Message-ID: <27C2D4B8.3AD3@tct.uucp> Date: 20 Feb 91 19:57:44 GMT References: <1991Feb13.011731.10114@gpu.utcs.utoronto.ca> <27BFD8E3.3D1D@tct.uucp> <1991Feb19.051123.5198@gpu.utcs.utoronto.ca> Organization: Teltronics/TCT, Sarasota, FL Lines: 115 According to craig@gpu.utcs.utoronto.ca (Craig Hubley): >The fact that C++ implements the two mechanisms [virtual functions and >overloaded functions] so as to create (in my opinion) counter-intuitive >effects is (in my opinion) a flaw in C++. That is, the programmer must >be very much aware of the "seam" between compile-time and run-time ... This fact is a *necessary* consequence of one of C++'s design goals, namely, that a programmer never pay more in efficiency than necessary. That means that it is the programmer who decides when to use run-time binding (virtual) and when to use compile-time binding (overloading). If you remove the distinction, you remove the control -- a tradeoff that is, to me, unacceptable. >Consider how static initialization works in C++. The initialization >syntax means that some functions consist entirely of initialization >and calls to these can (in principle) be resolved entirely at compile- >time if they are called with static arguments. Not so! Those initializations cannot, even in principle, be resolved at compile time if the actual types of their reference or pointer parameters are unknown at compile time -- which is exactly the situation for which virtual functions were created. So your scenario is only true in a "(C++)--" language without virtual functions. That's one way to merge two features: get rid of one of them. :-) >>a base class B and a derived class D: >> >> extern void foo(B&); >> extern void foo(D&); >> D d; B& b = d; foo(b); >> >>It is the "foo(B&)" function that will be called ... >> >> D d; B& b = d; b.foo(); >> >>It is B::foo() that is called ... > >In neither of the cases above can the system be expected to read the >programmer's mind and "decide" to call the function associated with >the "real" rather than the "formal" type. In fact, C++ explicitly uses >the -> vs. . notation so that the programmer can make that decision >him/herself. The notation is irrelevant. Given a pointer "a" with member "b", "a->b" and "(*a).b" are exactly equivalent in all cases. I suppose you knew that; so what did you really mean? >There is no "cast to the dynamic type" operator in C++ that would >delay this decision until run-time. You can't even find out what >the type is. That's a necessary result of the most basic type rules. And there's nothing special about overloaded functions in this context: normal functions and non-virtual member functions are in exactly the same boat. If dynamic type loss is unendurable to you, then you're using the wrong language, because it's not going away. >You have a good analogy there, but what you are proving is that overloading >is solving the same problem as member functions, but without the other half >of its brain: runtime resolution. When you mix virtuals and overloading, >things get scary ... "Well, don't do that, then." I would contend that, once virtual functions have been introduced into a class hierarchy, the additional use of overloaded functions is the design error to be corrected, not some presumed deficiency in C++. The differences between these techniques is a tool, not a flaw. As a practical implementation issue, it is obvious that non-friend non-member functions are indefinite in number. Presuming a vanilla implementation of virtual functions, how can a newly created object have a virtual function table containing the addresses of overloaded functions that haven't even been compiled yet? >But if "orthogonal" in this sense is supposed to mean "never interact >at all" you are wrong. But that's not what I meant, because that's not what the word means! Compare the Jargon File 2.6.3: orthogonal: [from mathematics] adj. Mutually independent; well separated; sometimes, irrelevant to. Used in a generalization of its mathematical meaning to describe sets of primitives or capabilities which, like a vector basis in geometry, span the entire `capability space' of the system and are in some sense non-overlapping or mutually independent. ... A language may have templates or not, and it may have inheritance or not; so the features are orgthogonal. But of course they interact; it's all one language. >As a programmer, I couldn't care less at what point in the >compile/link/load/run process these things are resolved, except >where the language forces me to be aware of it. Well, then, you're using the wrong language, or else you need to change your thinking. (No smiley here.) If your mental view of the type system does not include the distinction between compile time and run time, then C++ is simply a bad match for the way you think. As I mentioned up front, the *design goals* of C++ simply cannot allow all type resolution to be (conceptually) delayed until run time. Such a language wouldn't be C++ any more. >Obviously you have to be aware of these subtle differences sometimes but >inventing wholly different syntax for each situation is IMHO a mistake. Virtual and non-virtual member functions use the same syntax; the only difference is one keyword. However, once you decide: "But I don't want to use a member function," then you have stepped outside the bounds of "same things," so you shouldn't expect "same syntax." -- Chip Salzenberg at Teltronics/TCT , "It's not a security hole, it's a SECURITY ABYSS." -- Christoph Splittgerber (with reference to the upage bug in Interactive UNIX and Everex ESIX)