fox@allegra.att.com (David Fox) (08/15/90)
In porting code from g++ to cfront I have come across the following inconsistancy. The following program compiles as-is in g++ class A { public: void f(int); }; class B : public A { public: void f(char*); //void f(int i) {A::f(i);} }; main() { B x; x.f(3); } And the call to x.f(3) finds the function f in class A. In cfront, however, the function f in class B "masks" the f in class A, so you need the commented f(int) member function to get this to compile. To me, the g++ semantics make more sense. Do others agree?
880716a@aucs.uucp (Dave Astels) (08/17/90)
In article <FOX.90Aug15101045@devo.allegra.att.com> fox@allegra.att.com (David Fox) writes: >In porting code from g++ to cfront I have come across the following >inconsistancy. The following program compiles as-is in g++ > > class A { > public: > void f(int); > }; > > class B : public A { > public: > void f(char*); > //void f(int i) {A::f(i);} > }; > > main() > { > B x; > x.f(3); > } > >And the call to x.f(3) finds the function f in class A. In >cfront, however, the function f in class B "masks" the f in >class A, so you need the commented f(int) member function to >get this to compile. To me, the g++ semantics make more sense. >Do others agree? I feel as you do. B::f(char *) shouldn't override A::f(int). Unfortunately Zortech (2.06) doesn't aggree. It works as cfront does. I would think that overriding would take place using the mangled name. Thus f(int) would be completely different than f(char *). -- "I liked him better before he died" - McCoy, ST V =============================================================================== Dave Astels | Internet: 880716a@AcadiaU.CA PO Box 835, Wolfville, | Bitnet: 880716a@Acadia
mckenney@sparkyfs.istc.sri.com (Paul Mckenney) (08/18/90)
In article <FOX.90Aug15101045@devo.allegra.att.com> fox@allegra.att.com (David Fox) writes: >In porting code from g++ to cfront I have come across the following >inconsistancy. The following program compiles as-is in g++ > > class A { > public: > void f(int); > }; > > class B : public A { > public: > void f(char*); > //void f(int i) {A::f(i);} > }; > > main() > { > B x; > x.f(3); > } > >And the call to x.f(3) finds the function f in class A. In >cfront, however, the function f in class B "masks" the f in >class A, so you need the commented f(int) member function to >get this to compile. To me, the g++ semantics make more sense. >Do others agree? In Section 13.1 (page 311) of the Annotated C++ Reference Manual by Ellis and Stroustrup there is an explanation of cfront's hiding behavior and a rationale for it. The basic idea is that many programming styles result in fairly deep derivation trees, with the base and derived types scattered liberally through the program. In such a program, it can be difficult to keep track of all of the overloaded functions and thus it would difficult to determine the consequences of adding another overloaded function if this hiding did not occur. This restriction is not as onerous as it might be -- your example shows one way to get around it. Of course, this word-around can get tedious if you have classes with lots of overloaded member functions. One possibility would be to allow the programmer to specify the g++ semantics as follows: class A { public: void f(int); }; class B : public friend A { public: void f(char*); }; main() { B x; x.f(3); } The ``friend'' keyword in class B's base class list would tell the compiler that B's scope should include that of A, so that ``x.f(3)'' would find A::f. What do you think? Thanx, Paul
jbuck@galileo.berkeley.edu (Joe Buck) (08/18/90)
Sorry for the long quote. In article <1990Aug17.141710.5004@aucs.uucp>, 880716a@aucs.uucp (Dave Astels) writes: |> In article <FOX.90Aug15101045@devo.allegra.att.com> fox@allegra.att.com (David Fox) writes: |> >In porting code from g++ to cfront I have come across the following |> >inconsistancy. The following program compiles as-is in g++ |> > |> > class A { |> > public: |> > void f(int); |> > }; |> > |> > class B : public A { |> > public: |> > void f(char*); |> > //void f(int i) {A::f(i);} |> > }; |> > |> > main() |> > { |> > B x; |> > x.f(3); |> > } |> > |> >And the call to x.f(3) finds the function f in class A. In |> >cfront, however, the function f in class B "masks" the f in |> >class A, so you need the commented f(int) member function to |> >get this to compile. To me, the g++ semantics make more sense. |> >Do others agree? > I feel as you do. B::f(char *) shouldn't override A::f(int). > Unfortunately Zortech (2.06) doesn't aggree. It works as cfront does. > I would think that overriding would take place using the mangled name. > Thus f(int) would be completely different than f(char *). Unfortunately, E&S (which is the base document for the standard) mandates the cfront behavior. Unless this is changed, you can expect future versions of g++ to be changed to conform (since Michael Tiemann has promised to track the standard). Stroustrup justifies this by pointing out cases where the g++ behavior might lead to surprises when new functions are added, but I'm not swayed by his argument. -- Joe Buck jbuck@galileo.berkeley.edu {uunet,ucbvax}!galileo.berkeley.edu!jbuck
sakkinen@tukki.jyu.fi (Markku Sakkinen) (08/20/90)
In article <38219@ucbvax.BERKELEY.EDU> jbuck@galileo.berkeley.edu (Joe Buck) writes: >In article <1990Aug17.141710.5004@aucs.uucp>, 880716a@aucs.uucp (Dave >Astels) writes: >|> In article <FOX.90Aug15101045@devo.allegra.att.com> >fox@allegra.att.com (David Fox) writes: >|> >In porting code from g++ to cfront I have come across the following >|> >inconsistancy. [...] I.e. a function declaration in an inner scope hides all functions with the same name declared in outer scopes, independently of argument types. According to the Release 2.0 Ref.Man., Sc. 13.1, this applies even to other cases than base class - derived class. >|> [...] To me, the g++ semantics make more sense. >|> >Do others agree? > >> I feel as you do. B::f(char *) shouldn't override A::f(int). >> Unfortunately Zortech (2.06) doesn't aggree. It works as cfront does. > >> I would think that overriding would take place using the mangled name. >> Thus f(int) would be completely different than f(char *). > >Unfortunately, E&S (which is the base document for the standard) mandates >the cfront behavior. Unless this is changed, you can expect future >versions of g++ to be changed to conform (since Michael Tiemann has >promised to track the standard). > >Stroustrup justifies this by pointing out cases where the g++ behavior >might lead to surprises when new functions are added, but I'm not >swayed by his argument. This is an unorthogonal kludge, which makes the rules of a complicated language even more complicated. As far as I can see, it is also a deviation from older language specifications. Already the subtle difference between virtual and non-virtual overloading can be surprising enough; we don't need this hiding rule to bite us. If there are enough special cases to warrant this kind of behaviour, it should be declared with a new keyword, say "semipublic" instead of "public" base class. In the non-class-related case, the Ref.Man. example goes like this: int f (char *); void g () { extern f (int); ... } It would be more sensible if this implied only ordinary overloading, and an additional keyword ('hide') should be used in those weird cases where one really wants to hide all functions named 'f' in the outer scope. Markku Sakkinen Department of Computer Science University of Jyvaskyla (a's with umlauts) Seminaarinkatu 15 SF-40100 Jyvaskyla (umlauts again) Finland SAKKINEN@FINJYU.bitnet (alternative network address)
perry@key.COM (Perry The Cynic) (08/21/90)
In article <38219@ucbvax.BERKELEY.EDU> jbuck@galileo.berkeley.edu (Joe Buck) writes: > [Quote deleted, complaining that a function member definition hides > all functions of that name in parent classes, not just the one with > the specific parameter profile. Notes that g++ does not do that.] > > Unfortunately, E&S (which is the base document for the standard) mandates > the cfront behavior. Unless this is changed, you can expect future > versions of g++ to be changed to conform (since Michael Tiemann has > promised to track the standard). > > Stroustrup justifies this by pointing out cases where the g++ behavior > might lead to surprises when new functions are added, but I'm not > swayed by his argument. > Actually the argument goes like this: a group of overloaded functions is really intended to provide different *variants* of one functionality. Two functions without closely tied purpose and operation should not be overloaded (within the same class). Thus, if you redefine a function in a descendant class, you should make damn sure that the close relationship between the function variants in the parent is not violated (by failing to override one of them). At minimum, you should add inline members to the child to explicitly confirm that some variants of the parent are still applicable unchanged. Let's say there's this class Frob { public: void frobMe(void); void frobMe(int howOften); }; Frob frobber; where the frobMe(void) is trying to guess how often you want to frob, and then calls frobMe(int). If you create class MyFrob : public Frob { public: void frobMe(int moreOften); }; MyFrob frobber; (and neglect to redefine frobMe(void) as well), then somebody who is used to deal with a Frob may keep calling frobber.frobMe(void), if only because he didn't change his source but you changed yours. Of course frobMe(void) still calls the old frobMe(int), which is likely NOT what you intended. As things stand, the unsuspecting user of frobMe(void) will find that his source suddenly stops compiling, and he'll probably come battering down your door to demand an explanation. Of course you can argue that frobMe(int) should really be virtual. Maybe the rule should be relaxed to specify that only redeclaring non-virtual functions performs full hiding, but that would be another piece of user-visible (and, probably, user-confusing) complexity. As it stands, you can avoid these problems by judicious choice of (function) names. -- perry P.S.: I've worked in ADA, where relaxed one-variant-at-a-time overloading rules are in effect. Let me tell you that it's no fun trying to track down *which* fooBar(xyz) the compiler decided to call this time, especially with implicit conversions clouding the issue. By current C++ rules, the first (inner-most) class defining any fooBar member *must* be the one containing the thing you called. -- -------------------------------------------------------------------------- Perry The Cynic (Peter Kiehtreiber) perry@arkon.key.com ** What good signature isn't taken yet? ** {amdahl,sgi,pacbell}!key!perry
brucec@phoebus.phoebus.labs.tek.com (Bruce Cohen;;50-662;LP=A;) (08/21/90)
In article <1990Aug20.115115.3646@tukki.jyu.fi> sakkinen@tukki.jyu.fi (Markku Sakkinen) writes: > ... > > I.e. a function declaration in an inner scope hides all functions > with the same name declared in outer scopes, independently of > argument types. According to the Release 2.0 Ref.Man., Sc. 13.1, > this applies even to other cases than base class - derived class. > > ... > > This is an unorthogonal kludge, which makes the rules > of a complicated language even more complicated. > As far as I can see, it is also a deviation from older language > specifications. Already the subtle difference between virtual > and non-virtual overloading can be surprising enough; we don't > need this hiding rule to bite us. I agree that it's a kludge, and a dangererous one at that. Name hiding is one of the easiest ways to inadvertantly leave landmines in your code. Granted that someone will get it wrong no matter which way you standardize it, the best way is to remove complexity wherever possible, to make it easier for programmers to predict what will happen. > If there are enough special cases to warrant this kind of behaviour, > it should be declared with a new keyword, say "semipublic" Can anyone give me a case where the *programmer*, as opposed to the overprotective language designer, wants / needs this capability? Is there a case which can't be more cleanly handled with a disciplined use of scoping of a base class' members and controlled scoping of inheritance? -- --------------------------------------------------------------------------- NOTE: USE THIS ADDRESS TO REPLY, REPLY-TO IN HEADER MAY BE BROKEN! Bruce Cohen, Computer Research Lab email: brucec@tekcrl.labs.tek.com Tektronix Laboratories, Tektronix, Inc. phone: (503)627-5241 M/S 50-662, P.O. Box 500, Beaverton, OR 97077
jimad@microsoft.UUCP (Jim ADCOCK) (08/22/90)
In article <1990Aug20.115115.3646@tukki.jyu.fi> sakkinen@jytko.jyu.fi (Markku Sakkinen) writes: > >I.e. a function declaration in an inner scope hides all functions >with the same name declared in outer scopes, independently of >argument types. According to the Release 2.0 Ref.Man., Sc. 13.1, >this applies even to other cases than base class - derived class. > >This is an unorthogonal kludge, which makes the rules >of a complicated language even more complicated. >As far as I can see, it is also a deviation from older language >specifications. Already the subtle difference between virtual >and non-virtual overloading can be surprising enough; we don't >need this hiding rule to bite us. > I disagree. I support the interpretation that same-named functions with differing parameters should represent a function-set -- multiple implementations of the same behavior, only optimized for different pass parameters. If you want to implement differing functionality, use different function names, not parameters, to differentiate your routines. Thus, if one needs to override any one of the function-set in a derived class, it is pretty safe to assume all the other functions in the set, optimized on particular pass-parameter, need to be overridded too. In the odd case where a programmer decides one of the function-set does not need to be re-implemented, that function can be carried forward from the base class via a trivial in-line function. In any case, if you've got a lot of functions in your function-set, you're probably doing something wrong.